imerss-bioinfo 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -0
- package/build.js +196 -0
- package/docs/css/imerss-viz-all.css +1227 -0
- package/docs/css/imerss-viz-wp-overrides.css +70 -0
- package/docs/css/pepiowelh.css +76 -0
- package/docs/css/xetthecum-external.css +47 -0
- package/docs/css/xetthecum-shared.css +80 -0
- package/docs/css/xetthecum.css +526 -0
- package/docs/js/imerss-viz-all.js +58 -0
- package/docs/js/imerss-viz-all.js.map +1 -0
- package/docs/js/jquery.js +10881 -0
- package/index.html +81 -0
- package/index.js +7 -0
- package/indexWithJustMap.html +92 -0
- package/indexWithMap.html +79 -0
- package/indexWithMapHoweSound.html +91 -0
- package/indexWithMapPepiowelh.html +119 -0
- package/indexWithMapValdes.html +90 -0
- package/indexWithMapXetthecum.html +90 -0
- package/package.json +50 -0
- package/src/WoRMS/taxonAPI.js +13 -0
- package/src/arphify.js +662 -0
- package/src/assignNames.js +103 -0
- package/src/auxBuild/restoreJQuery.js +1 -0
- package/src/buildSource/Comprehensive Lists.html +42 -0
- package/src/buildSource/Data Paper Part I Visualisation.html +50 -0
- package/src/buildSource/Galiano Life List.html +43 -0
- package/src/buildSource/Metchosin/index.html +22 -0
- package/src/buildSource/Pe'pi'ow'elh.html +72 -0
- package/src/buildSource/Squamish Life List.html +48 -0
- package/src/buildSource/Valdes Island Biodiversity.html +64 -0
- package/src/buildSource/Xetthecum.html +40 -0
- package/src/buildSource/dataPaperSunburstAndMap.html +30 -0
- package/src/buildSource/index.html +224 -0
- package/src/client/css/imerss-viz-wp-overrides.css +70 -0
- package/src/client/css/imerss-viz.css +531 -0
- package/src/client/css/pepiowelh.css +76 -0
- package/src/client/css/xetthecum-external.css +47 -0
- package/src/client/css/xetthecum-shared.css +80 -0
- package/src/client/css/xetthecum.css +526 -0
- package/src/client/html/imerss-viz-map-only.html +75 -0
- package/src/client/html/imerss-viz-map-pepi.html +77 -0
- package/src/client/html/imerss-viz-map-xetthecum.html +90 -0
- package/src/client/html/imerss-viz-map.html +75 -0
- package/src/client/html/imerss-viz.html +58 -0
- package/src/client/html/importing Xetthecum patterns.txt +10 -0
- package/src/client/html/xetthecum-patterns.html +27 -0
- package/src/client/img/Animalia_2.svg +1 -0
- package/src/client/img/Animalia_Annelida.svg +655 -0
- package/src/client/img/Animalia_Chaetognatha.svg +72 -0
- package/src/client/img/Animalia_Cnidaria.svg +173 -0
- package/src/client/img/Animalia_Ctenophora.svg +298 -0
- package/src/client/img/Animalia_Nemertea.svg +54 -0
- package/src/client/img/Animalia_Phoronida.svg +271 -0
- package/src/client/img/Animalia_Platyhelminthes.svg +58 -0
- package/src/client/img/Animalia_Porifera.svg +220 -0
- package/src/client/img/Animalia_Sipuncula.svg +155 -0
- package/src/client/img/Arachnoidiscus_schmidtii_web_1-2-scaled.webp +0 -0
- package/src/client/img/Arrow-down.svg +11 -0
- package/src/client/img/Arrow-up.svg +6 -0
- package/src/client/img/Chromista.svg +1 -0
- package/src/client/img/Pepiowelh/BlackNoWe2-wb-bb.png +0 -0
- package/src/client/img/Pepiowelh/BlackNoWe2-wb.png +0 -0
- package/src/client/img/Pepiowelh/BlackNoWe2.png +0 -0
- package/src/client/img/Pepiowelh/Ecocultural Mapping Banner.svg +1 -0
- package/src/client/img/Plankton.jpg +0 -0
- package/src/client/img/Plantae.svg +1 -0
- package/src/client/img/Plantae_2.svg +1 -0
- package/src/client/img/Protista.svg +1 -0
- package/src/client/img/Valdes/Valdes Island Conservancy.jpg +0 -0
- package/src/client/img/Xetthecum/Agricultural.jpg +0 -0
- package/src/client/img/Xetthecum/Anthropogenic_banner.jpg +0 -0
- package/src/client/img/Xetthecum/Broad_Leaf.jpg +0 -0
- package/src/client/img/Xetthecum/Caves.jpg +0 -0
- package/src/client/img/Xetthecum/Coastal_Bluffs.jpg +0 -0
- package/src/client/img/Xetthecum/Conifer_Pole_Sapling.jpg +0 -0
- package/src/client/img/Xetthecum/Developed.jpg +0 -0
- package/src/client/img/Xetthecum/Dock.jpg +0 -0
- package/src/client/img/Xetthecum/Eelgrass.jpg +0 -0
- package/src/client/img/Xetthecum/Forests_banner.jpg +0 -0
- package/src/client/img/Xetthecum/Freshwater_banner.jpg +0 -0
- package/src/client/img/Xetthecum/Greig_Creek.jpg +0 -0
- package/src/client/img/Xetthecum/Intertidal.jpg +0 -0
- package/src/client/img/Xetthecum/Laughlin_Lake.jpg +0 -0
- package/src/client/img/Xetthecum/Marine_banner.jpg +0 -0
- package/src/client/img/Xetthecum/Mature_Conifer_Forest.jpg +0 -0
- package/src/client/img/Xetthecum/Porlier_Pass_Road.jpg +0 -0
- package/src/client/img/Xetthecum/Riparian.jpg +0 -0
- package/src/client/img/Xetthecum/Rural.jpg +0 -0
- package/src/client/img/Xetthecum/Subtidal.jpg +0 -0
- package/src/client/img/Xetthecum/Wetland.jpg +0 -0
- package/src/client/img/Xetthecum/Woodland.jpg +0 -0
- package/src/client/img/Xetthecum/Woodland_banner.jpg +0 -0
- package/src/client/img/Xetthecum/Xetthecum.jpg +0 -0
- package/src/client/img/Xetthecum/Young_Conifer_Forest.jpg +0 -0
- package/src/client/img/Xetthecum/Young_Mixed_Forest.jpg +0 -0
- package/src/client/img/Xetthecum/food.svg +38 -0
- package/src/client/img/Xetthecum/indicator.svg +52 -0
- package/src/client/img/Xetthecum/material.svg +186 -0
- package/src/client/img/Xetthecum/medicinal.svg +62 -0
- package/src/client/img/Xetthecum/missing-value.svg +3 -0
- package/src/client/img/Xetthecum/spiritual.svg +13 -0
- package/src/client/img/Xetthecum/trade.svg +74 -0
- package/src/client/img/audio.svg +5 -0
- package/src/client/img/external-link.svg +3 -0
- package/src/client/img/selected-eye.svg +4 -0
- package/src/client/img/selected-tick.svg +3 -0
- package/src/client/img/thumbs/BCLeps.jpg +0 -0
- package/src/client/img/thumbs/Checklist.jpg +0 -0
- package/src/client/img/thumbs/Data-Paper-Figma.jpg +0 -0
- package/src/client/img/thumbs/Galiano-Data-Paper.jpg +0 -0
- package/src/client/img/thumbs/Galiano.jpg +0 -0
- package/src/client/img/thumbs/Maxwell.jpg +0 -0
- package/src/client/img/thumbs/Squamish.jpg +0 -0
- package/src/client/img/thumbs/Valdes.jpg +0 -0
- package/src/client/img/thumbs/Xetthecum.jpg +0 -0
- package/src/client/img/up-arrow.svg +4 -0
- package/src/client/js/autocomplete.js +53 -0
- package/src/client/js/checklist.js +179 -0
- package/src/client/js/colour.js +139 -0
- package/src/client/js/datasetControls.js +212 -0
- package/src/client/js/imerss-viz.js +1397 -0
- package/src/client/js/leafletMap.js +357 -0
- package/src/client/js/leafletMapWithGrid.js +337 -0
- package/src/client/js/leafletMapWithRegions.js +381 -0
- package/src/client/js/renderSVG.js +108 -0
- package/src/client/js/tabs.js +53 -0
- package/src/client/js/xetthecum.js +106 -0
- package/src/client/json/emptyPhyloMap.json +1 -0
- package/src/client/json/kingdomPhyloMap.json +17 -0
- package/src/client/json/phyloMap.json +58 -0
- package/src/compare.js +97 -0
- package/src/compilecsv.js +47 -0
- package/src/dataProcessing/LZ4.js +27 -0
- package/src/dataProcessing/coordinatePatch.js +151 -0
- package/src/dataProcessing/readCSV.js +107 -0
- package/src/dataProcessing/readCSVwithMap.js +90 -0
- package/src/dataProcessing/readCSVwithoutMap.js +25 -0
- package/src/dataProcessing/readJSON.js +23 -0
- package/src/dataProcessing/sqlite.js +174 -0
- package/src/dataProcessing/summarise.js +122 -0
- package/src/dataProcessing/taxonScrape.js +14 -0
- package/src/dataProcessing/worker.js +31 -0
- package/src/dataProcessing/writeCSV.js +34 -0
- package/src/dataProcessing/writeJSON.js +15 -0
- package/src/dbtaxa.js +96 -0
- package/src/deAuthorise.js +48 -0
- package/src/deqgis.js +148 -0
- package/src/expressions/expr.js +65 -0
- package/src/expressions/jsep.js +925 -0
- package/src/filterFirst.js +51 -0
- package/src/geom/geoJSON.js +121 -0
- package/src/iNaturalist/examples/api.inaturalist.org-v1-taxa!q=Acmispon americanus americanus.json +200 -0
- package/src/iNaturalist/examples/api.inaturalist.org-v1-taxa!q=Flabellina japonica.json +61 -0
- package/src/iNaturalist/examples/www.inaturalist.org-taxa-877359.json +132 -0
- package/src/iNaturalist/iNatUrls.js +17 -0
- package/src/iNaturalist/obsAPI.js +150 -0
- package/src/iNaturalist/taxonAPI.js +460 -0
- package/src/inatObs.js +120 -0
- package/src/inattify.js +123 -0
- package/src/intersect.js +80 -0
- package/src/intersectFilter.js +48 -0
- package/src/landlocked.js +137 -0
- package/src/lib/jquery.binarytransport.js +51 -0
- package/src/lib/lz4-commentary.txt +9 -0
- package/src/lib/lz4.js +73 -0
- package/src/lib/point-in-polygon.js +36 -0
- package/src/lz4.js +40 -0
- package/src/marmalise.js +333 -0
- package/src/materialise.js +68 -0
- package/src/reintegrate.js +71 -0
- package/src/taxonomise.js +615 -0
- package/src/unscrewGBIF.js +44 -0
- package/src/utils/dataSource.js +173 -0
- package/src/utils/settleStructure.js +62 -0
- package/src/utils/utils.js +92 -0
- package/src/viz/plethify.js +39 -0
- package/src/wormify.js +154 -0
- package/src/xetthecum/xetthecumFilters.js +126 -0
- package/test/testDataSource.js +182 -0
- package/test/testiNatObsSource.js +38 -0
- package/test/testiNatSource.js +50 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/* global L */
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
// noinspection ES6ConvertVarToLetConst // otherwise this is a duplicate on minifying
|
|
6
|
+
var hortis = fluid.registerNamespace("hortis");
|
|
7
|
+
|
|
8
|
+
// Workaround taken from https://github.com/Leaflet/Leaflet/issues/4745 to prevent coordinates being rounded to
|
|
9
|
+
// integers during rendering
|
|
10
|
+
L.Map.include({
|
|
11
|
+
latLngToLayerPoint: function (latlng) {
|
|
12
|
+
const projectedPoint = this.project(L.latLng(latlng));
|
|
13
|
+
const togo = projectedPoint._subtract(this.getPixelOrigin());
|
|
14
|
+
togo.x = fluid.roundToDecimal(togo.x, 3);
|
|
15
|
+
togo.y = fluid.roundToDecimal(togo.y, 3);
|
|
16
|
+
return togo;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
fluid.defaults("hortis.leafletMap", {
|
|
21
|
+
gradeNames: ["fluid.viewComponent", "{sunburstLoader}.options.mapFlavourGrade"], // Not a distribution because of FLUID-5836
|
|
22
|
+
selectors: {
|
|
23
|
+
map: ".fld-imerss-map",
|
|
24
|
+
tooltip: ".fld-imerss-map-tooltip"
|
|
25
|
+
},
|
|
26
|
+
members: {
|
|
27
|
+
toPlot: {}, // General contract from grid map - indexed by "mapBlockId" to "bucket" - contains byTaxonId, count, Lpolygon (colours only used in drawGrid)
|
|
28
|
+
// byTaxonId is hash of rowId to array of obsId as built in hortis.quantiser.indexObs - so count is equal to # of keys
|
|
29
|
+
map: "@expand:L.map({that}.dom.map.0, {that}.options.mapOptions)"
|
|
30
|
+
},
|
|
31
|
+
datasets: {},
|
|
32
|
+
// Needs to be parameterised since leafletMapWithRegions and leafletMapWithGrid have different update semantics -
|
|
33
|
+
// regions is prone to an update cycle when clearing the selection and grid is not. Need to review this semantic
|
|
34
|
+
// carefully between the two variants
|
|
35
|
+
selectionTransactionSource: null,
|
|
36
|
+
model: {
|
|
37
|
+
mapInitialised: "@expand:{that}.events.buildMap.fire()",
|
|
38
|
+
// zoom: Number (0 - whole world -> 18 - maximal zoom)
|
|
39
|
+
datasetEnabled: "@expand:hortis.datasetEnabledModel({that}.options.datasets)",
|
|
40
|
+
// Terrible name which needs to be reviewed - common between map variants indicating "selected region"
|
|
41
|
+
mapBlockTooltipId: null
|
|
42
|
+
},
|
|
43
|
+
events: {
|
|
44
|
+
buildMap: null,
|
|
45
|
+
clearMapSelection: null
|
|
46
|
+
},
|
|
47
|
+
markup: {
|
|
48
|
+
tooltip: "<div class=\"fld-imerss-map-tooltip\"></div>",
|
|
49
|
+
grid: "<div class=\"fld-imerss-map-grid\"></div>",
|
|
50
|
+
tooltipHeader: "<table>",
|
|
51
|
+
tooltipRow: "<tr><td class=\"fl-taxonDisplay-key\">%key: </td><td class=\"fl-taxonDisplay-value\">%value</td>",
|
|
52
|
+
tooltipFooter: "</table>"
|
|
53
|
+
},
|
|
54
|
+
// fitBounds: [[48.855,-123.65],[49.005,-123.25]],
|
|
55
|
+
listeners: {
|
|
56
|
+
"buildMap.bindZoom": "hortis.leafletMap.bindZoom({that})",
|
|
57
|
+
"buildMap.fitBounds": "hortis.leafletMap.fitBounds({that}, {that}.options.fitBounds)",
|
|
58
|
+
"buildMap.createTooltip": "hortis.leafletMap.createTooltip({that}, {that}.options.markup)",
|
|
59
|
+
"buildMap.addTiles": "hortis.leafletMap.addTileLayer({that}.map, {that}.options.tileOptions)",
|
|
60
|
+
"clearMapSelection.mapBlock": {
|
|
61
|
+
changePath: "mapBlockTooltipId",
|
|
62
|
+
value: null
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
invokers: {
|
|
66
|
+
// Perhaps this will one day be a "materialiser registration" and we will instead call applier.pullModel("zoom")
|
|
67
|
+
acquireZoom: "hortis.leafletMap.acquireZoom({that})",
|
|
68
|
+
fitBounds: "hortis.leafletMap.fitBounds({that}, {arguments}.0, {arguments}.1)"
|
|
69
|
+
},
|
|
70
|
+
dynamicComponents: {
|
|
71
|
+
geoJSONLayers: { // Dumb, "global" geoJSON layers that are not interactive. Only historical example is json_Galiano_map_0, as per qgis2web exports
|
|
72
|
+
sources: "{that}.options.geoJSONMapLayers",
|
|
73
|
+
type: "hortis.geoJSONMapLayer",
|
|
74
|
+
options: {
|
|
75
|
+
layer: "{source}"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
mapOptions: {
|
|
80
|
+
zoomSnap: 0.1
|
|
81
|
+
},
|
|
82
|
+
heatLow: "#ffffff"
|
|
83
|
+
// heatHigh: "#ff0000",
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
fluid.defaults("hortis.leafletMapWithWE", {
|
|
87
|
+
selectors: {
|
|
88
|
+
WEOverlay: ".fld-imerss-we-overlay"
|
|
89
|
+
},
|
|
90
|
+
listeners: {
|
|
91
|
+
"buildMap.bindWEClick": "hortis.leafletMap.bindWEClick({that})"
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
hortis.leafletMap.bindWEClick = function (map) {
|
|
96
|
+
const overlay = map.locate("WEOverlay");
|
|
97
|
+
overlay.click(function () {
|
|
98
|
+
map.map.flyToBounds(hortis.projectBounds.Pepiowelh, {duration: 2});
|
|
99
|
+
overlay.css("opacity", 0);
|
|
100
|
+
overlay.css("pointer-events", "none");
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
hortis.leafletMap.acquireZoom = function (map) {
|
|
105
|
+
map.applier.change("zoom", map.map.getZoom());
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
hortis.leafletMap.bindZoom = function (map) {
|
|
109
|
+
const leafletMap = map.map;
|
|
110
|
+
// Note that we can't apply the change at startup because of FLUID-5498
|
|
111
|
+
leafletMap.on("zoomend", function () {
|
|
112
|
+
map.acquireZoom();
|
|
113
|
+
});
|
|
114
|
+
leafletMap.on("moveend", function () {
|
|
115
|
+
console.log("Bounds now ", leafletMap.getBounds());
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Mixin grades for leafletMap selecting different tile layers
|
|
120
|
+
fluid.defaults("hortis.streetmapTiles", {
|
|
121
|
+
tileOptions: {
|
|
122
|
+
tileUrl: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
123
|
+
tileAttribution: "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors"
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
fluid.defaults("hortis.GBIFTiles", {
|
|
128
|
+
tileOptions: {
|
|
129
|
+
tileUrl: "https://tile.gbif.org/3857/omt/{z}/{x}/{y}@1x.png?style=gbif-classic",
|
|
130
|
+
tileAttribution: "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors, © <a href=\"https://openmaptiles.org/\">OpenMapTiles</a>, <a href=\"/citation-guidelines\">GBIF</a>"
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
fluid.defaults("hortis.MtbTiles", {
|
|
135
|
+
tileOptions: {
|
|
136
|
+
tileUrl: "http://tile.mtbmap.cz/mtbmap_tiles/{z}/{x}/{y}.png",
|
|
137
|
+
tileAttribution: "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors & USGS"
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
hortis.leafletMap.addTileLayer = function (map, tileOptions) {
|
|
142
|
+
if (tileOptions && tileOptions.tileUrl) {
|
|
143
|
+
L.tileLayer(tileOptions.tileUrl, {
|
|
144
|
+
attribution: tileOptions.tileAttribution
|
|
145
|
+
}).addTo(map);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
hortis.datasetEnabledModel = function (datasets) {
|
|
150
|
+
return fluid.transform(datasets, function () {
|
|
151
|
+
return true;
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
fluid.defaults("hortis.geoJSONMapLayer", {
|
|
156
|
+
gradeNames: "fluid.component",
|
|
157
|
+
style: {
|
|
158
|
+
color: "black",
|
|
159
|
+
weight: 1.0
|
|
160
|
+
},
|
|
161
|
+
listeners: {
|
|
162
|
+
"onCreate.applyLayer": "hortis.applyGeoJSONMapLayer({that}, {hortis.leafletMap})"
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
hortis.applyGeoJSONMapLayer = function (mapLayer, map) {
|
|
167
|
+
L.geoJSON(mapLayer.options.layer, {
|
|
168
|
+
style: mapLayer.options.style
|
|
169
|
+
}).addTo(map.map);
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
hortis.leafletMap.fitBounds = function (map, fitBounds) {
|
|
173
|
+
if (fitBounds) {
|
|
174
|
+
map.map.fitBounds(fitBounds);
|
|
175
|
+
map.acquireZoom();
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
hortis.leafletMap.createTooltip = function (that, markup) {
|
|
180
|
+
const tooltip = $(markup.tooltip).appendTo(that.locate("map"));
|
|
181
|
+
tooltip.hide();
|
|
182
|
+
that.map.createPane("hortis-tooltip", tooltip[0]);
|
|
183
|
+
const container = that.map.getContainer();
|
|
184
|
+
$(container).on("click", function (event) {
|
|
185
|
+
if (event.target === container) {
|
|
186
|
+
that.events.clearMapSelection.fire();
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
$(document).on("click", function (event) {
|
|
190
|
+
const closest = event.target.closest(".fld-imerss-nodismiss-map");
|
|
191
|
+
// Mysteriously SVG paths are not in the document
|
|
192
|
+
if (!closest && event.target.closest("body")) {
|
|
193
|
+
that.events.clearMapSelection.fire();
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
hortis.projectBounds = {
|
|
200
|
+
Galiano: [[48.855, -123.65], [49.005, -123.25]],
|
|
201
|
+
Valdes: [[49.000, -123.798], [49.144, -123.504]],
|
|
202
|
+
Xetthecum: [[48.93713, -123.5110], [48.9511, -123.4980]],
|
|
203
|
+
Pepiowelh: [[48.5650, -123.1575], [48.5980, -123.1266]],
|
|
204
|
+
HoweSound: [[49.160, -124.281], [50.170, -122.050]],
|
|
205
|
+
SalishSea: [[47.568, -124.200], [49.134, -122.059]]
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
fluid.defaults("hortis.sunburstLoaderWithMap", {
|
|
209
|
+
gradeNames: "hortis.sunburstLoader",
|
|
210
|
+
selectors: {
|
|
211
|
+
mapHolder: ".fld-imerss-map-holder"
|
|
212
|
+
},
|
|
213
|
+
events: {
|
|
214
|
+
sunburstLoaded: null
|
|
215
|
+
},
|
|
216
|
+
mapBounds: hortis.projectBounds.Galiano,
|
|
217
|
+
mapGrades: [],
|
|
218
|
+
mapFlavourGrade: "hortis.leafletMap.withGrid", // swap out for "withRegions" for Xetthecum style map
|
|
219
|
+
markupTemplate: "%resourceBase/html/imerss-viz-map.html",
|
|
220
|
+
distributeOptions: {
|
|
221
|
+
sunburstLoaded: {
|
|
222
|
+
target: "{that sunburst}.options.listeners.onCreate",
|
|
223
|
+
record: "{hortis.sunburstLoaderWithMap}.events.sunburstLoaded.fire"
|
|
224
|
+
},
|
|
225
|
+
flatTree: {
|
|
226
|
+
target: "{that quantiser}.options.members.flatTree",
|
|
227
|
+
record: "@expand:fluid.identity({sunburst}.flatTree)"
|
|
228
|
+
},
|
|
229
|
+
mapGrades: {
|
|
230
|
+
target: "{that map}.options.gradeNames",
|
|
231
|
+
source: "{that}.options.mapGrades"
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
components: {
|
|
235
|
+
map: {
|
|
236
|
+
type: "hortis.leafletMap",
|
|
237
|
+
container: "{sunburstLoaderWithMap}.dom.mapHolder",
|
|
238
|
+
createOnEvent: "sunburstLoaded",
|
|
239
|
+
options: {
|
|
240
|
+
gradeNames: "hortis.mapWithSunburst",
|
|
241
|
+
fitBounds: "{hortis.configHolder}.options.mapBounds",
|
|
242
|
+
showObsListInTooltip: "{hortis.configHolder}.options.showObsListInTooltip"
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Mixin grade for sunburstLoader, currently used in Xetthecum driver
|
|
249
|
+
fluid.defaults("hortis.svgPatternLoader", {
|
|
250
|
+
svgPatterns: "%resourceBase/html/xetthecum-patterns.html",
|
|
251
|
+
resources: {
|
|
252
|
+
svgPatterns: {
|
|
253
|
+
url: "{that}.options.svgPatterns",
|
|
254
|
+
dataType: "text"
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
listeners: {
|
|
258
|
+
"onResourcesLoaded.injectPatterns": {
|
|
259
|
+
funcName: "hortis.injectSvgPatterns",
|
|
260
|
+
args: ["{svgPatternLoader}.resources.svgPatterns.resourceText", "{that}.options.resourceBase", "{that}.container"]
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
hortis.injectSvgPatterns = function (patternText, resourceBase, container) {
|
|
266
|
+
const expanded = patternText.replace(/%resourceBase/g, resourceBase);
|
|
267
|
+
const patterns = $(expanded);
|
|
268
|
+
container.append(patterns);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
fluid.defaults("hortis.mapLoaderWithoutSunburst", {
|
|
272
|
+
// TODO: Refactor this obvious insanity
|
|
273
|
+
gradeNames: "hortis.sunburstLoaderWithMap",
|
|
274
|
+
markupTemplate: "%resourceBase/html/imerss-viz-map-only.html",
|
|
275
|
+
components: {
|
|
276
|
+
sunburst: {
|
|
277
|
+
options: {
|
|
278
|
+
model: {
|
|
279
|
+
visible: false
|
|
280
|
+
},
|
|
281
|
+
components: {
|
|
282
|
+
autocomplete: {
|
|
283
|
+
type: "fluid.emptySubcomponent"
|
|
284
|
+
},
|
|
285
|
+
tabs: {
|
|
286
|
+
type: "fluid.emptySubcomponent"
|
|
287
|
+
},
|
|
288
|
+
checklist: {
|
|
289
|
+
type: "fluid.emptySubcomponent"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
fluid.defaults("hortis.mapWithSunburst", {
|
|
298
|
+
modelListeners: {
|
|
299
|
+
mapFocusedTooltipToSunburst: {
|
|
300
|
+
path: "{map}.model.mapBlockTooltipId",
|
|
301
|
+
func: "hortis.mapBlockToFocusedTaxa",
|
|
302
|
+
args: ["{change}.value", "{map}", "{sunburst}"]
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
datasets: "{sunburst}.viz.datasets",
|
|
306
|
+
geoJSONMapLayers: "{sunburstLoaderWithMap}.options.geoJSONMapLayers"
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Can't use modelRelay because of https://issues.fluidproject.org/browse/FLUID-6208
|
|
310
|
+
hortis.mapBlockToFocusedTaxa = function (mapBlockTooltipId, map, sunburst) {
|
|
311
|
+
const togo = {};
|
|
312
|
+
if (mapBlockTooltipId) {
|
|
313
|
+
const bucket = map.toPlot[mapBlockTooltipId];
|
|
314
|
+
if (bucket) {
|
|
315
|
+
fluid.each(bucket.byTaxonId, function (obs, taxonId) {
|
|
316
|
+
togo[taxonId] = true;
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const source = map.options.selectionTransactionSource;
|
|
321
|
+
// As transaction to avoid triggering hortis.updateRowFocus twice which then invokes beginZoom
|
|
322
|
+
const trans = sunburst.applier.initiate(source);
|
|
323
|
+
trans.change("rowFocus", null, "DELETE", source);
|
|
324
|
+
trans.change("rowFocus", togo, null, source);
|
|
325
|
+
trans.commit();
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// From https://en.wikipedia.org/wiki/Longitude#Length_of_a_degree_of_longitude
|
|
329
|
+
hortis.WGS84a = 6378137;
|
|
330
|
+
hortis.WGS84b = 6356752.3142;
|
|
331
|
+
hortis.WGS84e2 = (hortis.WGS84a * hortis.WGS84a - hortis.WGS84b * hortis.WGS84b) / (hortis.WGS84a * hortis.WGS84a);
|
|
332
|
+
|
|
333
|
+
/** Length in metres for a degree of longitude at given latitude **/
|
|
334
|
+
|
|
335
|
+
hortis.longitudeLength = function (latitude) {
|
|
336
|
+
const latrad = Math.PI * latitude / 180;
|
|
337
|
+
const sinrad = Math.sin(latrad);
|
|
338
|
+
return Math.PI * hortis.WGS84a * Math.cos(latrad) / (180 * Math.sqrt(1 - hortis.WGS84e2 * sinrad * sinrad));
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
/** Length in metres for a degree of latitude at given latitude **/
|
|
342
|
+
|
|
343
|
+
hortis.latitudeLength = function (latitude) {
|
|
344
|
+
const latrad = Math.PI * latitude / 180;
|
|
345
|
+
const sinrad = Math.sin(latrad);
|
|
346
|
+
return Math.PI * hortis.WGS84a * (1 - hortis.WGS84e2) / (180 * Math.pow(1 - hortis.WGS84e2 * sinrad * sinrad, 1.5));
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
hortis.longToLat = function (lng, lat) {
|
|
350
|
+
const longLength = hortis.longitudeLength(lat);
|
|
351
|
+
const latLength = hortis.latitudeLength(lat);
|
|
352
|
+
return lng * longLength / latLength;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
hortis.quantiserDataset = function () {
|
|
356
|
+
return {maxCount: 0, totalCount: 0, buckets: {}, byTaxonId: {}};
|
|
357
|
+
};
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/* global L */
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
// noinspection ES6ConvertVarToLetConst // otherwise this is a duplicate on minifying
|
|
6
|
+
var hortis = fluid.registerNamespace("hortis");
|
|
7
|
+
|
|
8
|
+
fluid.defaults("hortis.leafletMap.withGrid", {
|
|
9
|
+
selectors: {
|
|
10
|
+
grid: ".fld-imerss-map-grid",
|
|
11
|
+
datasetControls: ".fld-imerss-dataset-controls",
|
|
12
|
+
datasetsLabel: ".fld-imerss-datasets-label"
|
|
13
|
+
},
|
|
14
|
+
baseZoomForOutline: 11.9,
|
|
15
|
+
model: {
|
|
16
|
+
datasetsLabel: "Data Sources:"
|
|
17
|
+
},
|
|
18
|
+
modelListeners: {
|
|
19
|
+
drawGrid: {
|
|
20
|
+
path: ["indexVersion", "datasetEnabled"],
|
|
21
|
+
func: "{that}.drawGrid"
|
|
22
|
+
},
|
|
23
|
+
zoomToOutline: {
|
|
24
|
+
path: "zoom",
|
|
25
|
+
listener: "hortis.leafletMap.updateOutlineWidth",
|
|
26
|
+
args: ["{change}.value", "{that}.options.baseZoomForOutline", "{that}.container.0"]
|
|
27
|
+
},
|
|
28
|
+
drawScale: {
|
|
29
|
+
path: "{quantiser}.model.squareSide",
|
|
30
|
+
func: "hortis.leafletMap.drawScale",
|
|
31
|
+
args: ["{that}", "{change}.value"]
|
|
32
|
+
},
|
|
33
|
+
updateTooltip: {
|
|
34
|
+
path: ["mapBlockTooltipId", "indexVersion", "datasetEnabled"],
|
|
35
|
+
priority: "after:drawGrid",
|
|
36
|
+
excludeSource: "init",
|
|
37
|
+
func: "hortis.leafletMap.withGrid.updateTooltip",
|
|
38
|
+
args: ["{that}", "{that}.model.mapBlockTooltipId"]
|
|
39
|
+
},
|
|
40
|
+
updateTooltipHighlight: {
|
|
41
|
+
path: "mapBlockTooltipId",
|
|
42
|
+
excludeSource: "init",
|
|
43
|
+
func: "hortis.leafletMap.withGrid.updateTooltipHighlight",
|
|
44
|
+
args: ["{that}", "{change}.oldValue"]
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
invokers: {
|
|
48
|
+
drawGrid: "hortis.leafletMap.drawGrid({that}, {that}.quantiser, {that}.model.datasetEnabled)"
|
|
49
|
+
},
|
|
50
|
+
gridStyle: {
|
|
51
|
+
className: "fld-imerss-map-region"
|
|
52
|
+
},
|
|
53
|
+
listeners: {
|
|
54
|
+
"buildMap.addGrid": "hortis.leafletMap.withGrid.addGrid({that})"
|
|
55
|
+
},
|
|
56
|
+
modelRelay: {
|
|
57
|
+
renderDatasetControls: { // This could be refined someday if we are ever required to produce dataset controls with non-square map regions
|
|
58
|
+
target: "datasetControls",
|
|
59
|
+
func: "hortis.renderDatasetControls",
|
|
60
|
+
args: ["{that}.model.datasetEnabled", "{that}.quantiser.model.squareSide", "{that}.options.datasets", "{that}.quantiser", "{that}.model.indexVersion"]
|
|
61
|
+
},
|
|
62
|
+
datasetsLabel: {
|
|
63
|
+
source: "datasetsLabel",
|
|
64
|
+
target: "dom.datasetsLabel.text"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
components: {
|
|
68
|
+
quantiser: {
|
|
69
|
+
type: "hortis.quantiser",
|
|
70
|
+
options: {
|
|
71
|
+
baseLatitude: "{leafletMap}.options.fitBounds.0.0",
|
|
72
|
+
datasets: "{leafletMap}.options.datasets",
|
|
73
|
+
model: {
|
|
74
|
+
indexVersion: "{leafletMap}.model.indexVersion"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
dynamicComponents: {
|
|
80
|
+
datasetControls: {
|
|
81
|
+
sources: "{that}.model.datasetControls",
|
|
82
|
+
type: "{source}.type",
|
|
83
|
+
options: {
|
|
84
|
+
model: {
|
|
85
|
+
datasetControl: "{source}"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
hortis.leafletMap.updateOutlineWidth = function (zoom, baseZoom, container) {
|
|
93
|
+
const relativeZoom = Math.min(1, Math.pow(2, 2 * (zoom - baseZoom)));
|
|
94
|
+
container.style.setProperty("--imerss-stroke-width", 2 * relativeZoom);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
hortis.leafletMap.withGrid.addGrid = function (that) {
|
|
98
|
+
that.map.createPane("hortis-grid");
|
|
99
|
+
that.gridGroup = L.layerGroup({pane: "hortis-grid"}).addTo(that.map);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
hortis.leafletMap.drawScale = function (map, squareSide) {
|
|
103
|
+
const dimText = squareSide.toFixed(0) + "m";
|
|
104
|
+
map.container.find(".leaflet-bottom.leaflet-left").text("Block size: " + dimText + " x " + dimText);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
hortis.rectFromCorner = function (tl, latres, longres) {
|
|
108
|
+
return [
|
|
109
|
+
[tl[0], tl[1]],
|
|
110
|
+
[tl[0], tl[1] + longres],
|
|
111
|
+
[tl[0] + latres, tl[1] + longres],
|
|
112
|
+
[tl[0] + latres, tl[1]]
|
|
113
|
+
];
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
hortis.leafletMap.drawGrid = function (map, quantiser, datasetEnabled) {
|
|
117
|
+
map.gridGroup.clearLayers();
|
|
118
|
+
|
|
119
|
+
const latres = quantiser.model.latResolution, longres = quantiser.model.longResolution;
|
|
120
|
+
const heatLow = fluid.colour.hexToArray(map.options.heatLow);
|
|
121
|
+
const toPlot = map.toPlot = {};
|
|
122
|
+
fluid.each(quantiser.datasets, function (dataset, datasetId) {
|
|
123
|
+
if (datasetEnabled[datasetId]) {
|
|
124
|
+
const mapDataset = map.options.datasets[datasetId];
|
|
125
|
+
const heatHigh = fluid.colour.hexToArray(mapDataset.colour);
|
|
126
|
+
fluid.each(dataset.buckets, function (bucket, key) {
|
|
127
|
+
const prop = Math.pow(bucket.count / dataset.maxCount, 0.25);
|
|
128
|
+
const fillColour = fluid.colour.interpolate(prop, heatLow, heatHigh);
|
|
129
|
+
fluid.model.setSimple(toPlot, [key, "colours", datasetId], fillColour);
|
|
130
|
+
const plotBucket = toPlot[key];
|
|
131
|
+
plotBucket.count = plotBucket.count || 0;
|
|
132
|
+
plotBucket.count += bucket.count;
|
|
133
|
+
plotBucket.byTaxonId = fluid.extend({}, plotBucket.byTaxonId, bucket.byTaxonId);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
fluid.each(toPlot, function (bucket, key) {
|
|
138
|
+
const colours = fluid.values(bucket.colours);
|
|
139
|
+
const colour = fluid.colour.average(colours);
|
|
140
|
+
const topLeft = hortis.quantiser.indexToCoord(key, latres, longres);
|
|
141
|
+
bucket.polygon = hortis.rectFromCorner(topLeft, latres, longres);
|
|
142
|
+
bucket.Lpolygon = L.polygon(bucket.polygon, fluid.extend({}, map.options.gridStyle, {
|
|
143
|
+
fillColor: fluid.colour.arrayToString(colour),
|
|
144
|
+
pane: "hortis-grid"
|
|
145
|
+
}));
|
|
146
|
+
map.gridGroup.addLayer(bucket.Lpolygon);
|
|
147
|
+
bucket.Lpolygon.on("click", function () {
|
|
148
|
+
console.log("Map clicked on key ", key);
|
|
149
|
+
map.applier.change("mapBlockTooltipId", key);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
// Deal with overall loss of a block through not appearing in dataset selection
|
|
153
|
+
if (!toPlot[map.model.mapBlockTooltipId]) {
|
|
154
|
+
map.applier.change("mapBlockTooltipId", null);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
hortis.leafletMap.tooltipRow = function (map, key, value) {
|
|
159
|
+
return fluid.stringTemplate(map.options.markup.tooltipRow, {key: key, value: value});
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// TODO: Design fault, only responsible for REMOVING highlight, adding of highlight occurs in updateTooltip
|
|
163
|
+
hortis.leafletMap.withGrid.updateTooltipHighlight = function (map, oldKey) {
|
|
164
|
+
if (oldKey) {
|
|
165
|
+
const oldBucket = map.toPlot[oldKey];
|
|
166
|
+
if (oldBucket) {
|
|
167
|
+
const element = oldBucket.Lpolygon.getElement();
|
|
168
|
+
element.classList.remove("fl-imerss-highlightBlock");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// It is called "tooltip" but actually it is a popup panel activated on click
|
|
174
|
+
hortis.leafletMap.withGrid.updateTooltip = function (map, key) {
|
|
175
|
+
const tooltip = map.locate("tooltip");
|
|
176
|
+
const bucket = map.toPlot[key];
|
|
177
|
+
if (bucket) {
|
|
178
|
+
let text = map.options.markup.tooltipHeader;
|
|
179
|
+
const dumpRow = function (key, value) {
|
|
180
|
+
text += hortis.leafletMap.tooltipRow(map, key, value);
|
|
181
|
+
};
|
|
182
|
+
const resolution = map.quantiser.model.longResolution;
|
|
183
|
+
const dp = Math.max(3, 1 - Math.log10(resolution));
|
|
184
|
+
const c = function (value) {
|
|
185
|
+
return value.toFixed(dp);
|
|
186
|
+
};
|
|
187
|
+
dumpRow("Observation Count", bucket.count);
|
|
188
|
+
dumpRow("Species Richness", Object.values(bucket.byTaxonId).length);
|
|
189
|
+
const p = bucket.polygon;
|
|
190
|
+
const lat0 = p[0][0], lat1 = p[2][0];
|
|
191
|
+
const lng0 = p[0][1], lng1 = p[1][1];
|
|
192
|
+
dumpRow("Latitude", c(lat0) + " to " + c(lat1));
|
|
193
|
+
dumpRow("Longitude", c(lng0) + " to " + c(lng1));
|
|
194
|
+
if (bucket.count < 5 && map.options.showObsListInTooltip) {
|
|
195
|
+
const obs = fluid.flatten(Object.values(bucket.byTaxonId));
|
|
196
|
+
const obsString = fluid.transform(obs, hortis.renderObsId).map(s => "iNaturalist: " + s).join("<br/>");
|
|
197
|
+
dumpRow("Observations", obsString);
|
|
198
|
+
}
|
|
199
|
+
text += map.options.markup.tooltipFooter;
|
|
200
|
+
tooltip[0].innerHTML = text;
|
|
201
|
+
tooltip.show();
|
|
202
|
+
const element = bucket.Lpolygon.getElement();
|
|
203
|
+
element.classList.add("fl-imerss-highlightBlock");
|
|
204
|
+
const parent = element.parentNode;
|
|
205
|
+
parent.insertBefore(element, null);
|
|
206
|
+
} else {
|
|
207
|
+
tooltip.hide();
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
fluid.defaults("hortis.quantiser", {
|
|
213
|
+
gradeNames: "fluid.modelComponent",
|
|
214
|
+
baseLatitude: 51,
|
|
215
|
+
model: {
|
|
216
|
+
longResolution: 0.005,
|
|
217
|
+
indexVersion: 0
|
|
218
|
+
},
|
|
219
|
+
modelRelay: {
|
|
220
|
+
latResolution: {
|
|
221
|
+
target: "latResolution",
|
|
222
|
+
singleTransform: {
|
|
223
|
+
type: "fluid.transforms.free",
|
|
224
|
+
args: ["{that}.model.longResolution", "{that}.options.baseLatitude"],
|
|
225
|
+
func: "hortis.longToLat"
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
squareSide: {
|
|
229
|
+
target: "squareSide",
|
|
230
|
+
singleTransform: {
|
|
231
|
+
type: "fluid.transforms.free",
|
|
232
|
+
func: "hortis.quantiser.squareSide",
|
|
233
|
+
args: ["{that}.options.baseLatitude", "{that}.model.latResolution"]
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
index: {
|
|
237
|
+
target: "indexVersion",
|
|
238
|
+
singleTransform: {
|
|
239
|
+
type: "fluid.transforms.free",
|
|
240
|
+
args: ["{that}", "{that}.options.datasets", "{that}.model.latResolution", "{that}.model.longResolution", "{that}.model.squareSide", "{that}.model.indexVersion"],
|
|
241
|
+
func: "hortis.quantiser.indexTree"
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
members: {
|
|
246
|
+
datasets: {
|
|
247
|
+
// hash of datasetId to {maxCount, buckets}
|
|
248
|
+
// where buckets is hash of id to {count, byId}
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
events: {
|
|
252
|
+
indexUpdated: null
|
|
253
|
+
},
|
|
254
|
+
invokers: {
|
|
255
|
+
datasetToSummary: "hortis.quantiser.datasetToSummary({arguments}.0, {arguments}.1)", // bucket - will be modified
|
|
256
|
+
indexObs: "hortis.quantiser.indexObs({that}, {arguments}.0, {arguments}.1, {arguments}.2, {arguments}.3, {arguments}.4)" // coord, obsId, id, latRes, longRes
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
hortis.quantiser.squareSide = function (baseLatitude, latResolution) {
|
|
261
|
+
return hortis.latitudeLength(baseLatitude) * latResolution;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
hortis.quantiser.indexToCoord = function (index, latres, longres) {
|
|
265
|
+
const coords = index.split("|");
|
|
266
|
+
return [coords[0] * latres, coords[1] * longres];
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
hortis.quantiser.coordToIndex = function (coord, latres, longres) {
|
|
270
|
+
if (coord) {
|
|
271
|
+
const lat = Math.floor(coord[0] / latres);
|
|
272
|
+
const lng = Math.floor(coord[1] / longres);
|
|
273
|
+
return lat + "|" + lng;
|
|
274
|
+
} else {
|
|
275
|
+
return "null";
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
hortis.datasetIdFromObs = function (obsId) {
|
|
280
|
+
const colpos = obsId.indexOf(":");
|
|
281
|
+
return obsId.substring(0, colpos);
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
hortis.localIdFromObs = function (obsId) {
|
|
285
|
+
const colpos = obsId.indexOf(":");
|
|
286
|
+
return obsId.substring(colpos + 1);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
hortis.quantiser.indexObs = function (that, coord, obsId, rowId, latResolution, longResolution) {
|
|
290
|
+
const coordIndex = hortis.quantiser.coordToIndex(coord, latResolution, longResolution);
|
|
291
|
+
const datasetId = hortis.datasetIdFromObs(obsId);
|
|
292
|
+
const dataset = that.datasets[datasetId];
|
|
293
|
+
if (!dataset) {
|
|
294
|
+
fluid.fail("Found observation with unknown dataset " + datasetId);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
dataset.byTaxonId[rowId] = true;
|
|
298
|
+
dataset.totalCount++;
|
|
299
|
+
if (coordIndex !== "null") {
|
|
300
|
+
let bucket = dataset.buckets[coordIndex];
|
|
301
|
+
if (!bucket) {
|
|
302
|
+
bucket = dataset.buckets[coordIndex] = {count: 0, byTaxonId: {}};
|
|
303
|
+
}
|
|
304
|
+
bucket.count++;
|
|
305
|
+
dataset.maxCount = Math.max(dataset.maxCount, bucket.count);
|
|
306
|
+
let bucketTaxa = bucket.byTaxonId[rowId]; // TODO: use fluid.pushArray?
|
|
307
|
+
if (!bucketTaxa) {
|
|
308
|
+
bucketTaxa = bucket.byTaxonId[rowId] = [];
|
|
309
|
+
}
|
|
310
|
+
bucketTaxa.push(obsId);
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
hortis.quantiser.datasetToSummary = function (dataset, squareSide) {
|
|
315
|
+
const squareArea = squareSide * squareSide / (1000 * 1000);
|
|
316
|
+
dataset.taxaCount = Object.keys(dataset.byTaxonId).length;
|
|
317
|
+
dataset.area = (Object.keys(dataset.buckets).length * squareArea).toFixed(2);
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
hortis.quantiser.indexTree = function (that, datasets, latResolution, longResolution, squareSide, indexVersion) {
|
|
321
|
+
that.datasets = fluid.transform(datasets, hortis.quantiserDataset);
|
|
322
|
+
for (let i = that.flatTree.length - 1; i >= 0; --i) {
|
|
323
|
+
const row = that.flatTree[i];
|
|
324
|
+
if (row.coords) {
|
|
325
|
+
const coords = JSON.parse(row.coords);
|
|
326
|
+
fluid.each(coords, function (coord, obsId) {
|
|
327
|
+
that.indexObs(coord, obsId, row.id, latResolution, longResolution);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
fluid.each(that.datasets, function (dataset) {
|
|
333
|
+
that.datasetToSummary(dataset, squareSide);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
return indexVersion + 1;
|
|
337
|
+
};
|