@superrouteca/smap 1.0.6 → 1.1.1
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/CHANGELOG.md +31 -0
- package/README.md +176 -3
- package/dist/SOURCE.json +10 -10
- package/dist/smap-client.cjs +17 -3
- package/dist/smap-client.d.cts +6 -0
- package/dist/smap-client.d.mts +6 -0
- package/dist/smap-client.d.ts +6 -0
- package/dist/smap-client.js +17 -3
- package/dist/smap-client.mjs +17 -3
- package/dist/smap.cjs +2222 -33
- package/dist/smap.js +2193 -30
- package/dist/smap.mjs +2211 -34
- package/package.json +12 -1
- package/types/smap.d.cts +299 -5
- package/types/smap.d.mts +299 -5
package/dist/smap.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Generated by js/build.mjs from js/src (see SOURCE.json). Do not edit. */
|
|
2
2
|
'use strict';
|
|
3
|
-
/*! sMap Web SDK 1.
|
|
3
|
+
/*! sMap Web SDK 1.1.1. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
|
|
4
4
|
|
|
5
5
|
var smapCore = null;
|
|
6
6
|
function smapCreateEngine() {
|
|
@@ -26849,12 +26849,1491 @@ ${o2.prelude.vertexSource}`, define: o2.define }, defaultProjectionData: d2, get
|
|
|
26849
26849
|
smapEngine.setWorkerUrl(smapWorkerUrl);
|
|
26850
26850
|
return smapEngine;
|
|
26851
26851
|
}
|
|
26852
|
-
function
|
|
26852
|
+
function smapCreateInterop() {
|
|
26853
|
+
var module = { exports: {} };
|
|
26854
|
+
var exports = module.exports;
|
|
26855
|
+
var define;
|
|
26856
|
+
var self = {};
|
|
26857
|
+
/*!
|
|
26858
|
+
* sMap Web SDK — the parts that need no map: basemap classes and overrides, slots, file formats (read and write),
|
|
26859
|
+
* the converting endpoint, raster tile URLs, PMTiles archives and WMTS capabilities. Pure functions, usable on a
|
|
26860
|
+
* server; the map view (smap.js) is built on them. Rules shared by every SDK: smap-sdk shared/INTEROP-CONTRACT.md,
|
|
26861
|
+
* shared/basemap-classes.json, shared/FORMAT-RULES.md, shared/LAYER-JSON.md.
|
|
26862
|
+
*/
|
|
26863
|
+
(function (root, factory) {
|
|
26864
|
+
if (typeof module === 'object' && module.exports) { module.exports = factory(); } else { root.smapInterop = factory(); }
|
|
26865
|
+
})(typeof self !== 'undefined' ? self : this, function () {
|
|
26866
|
+
'use strict';
|
|
26867
|
+
|
|
26868
|
+
var DEFAULT_BASE = 'https://tile.srsr.ca';
|
|
26869
|
+
var FLAVORS = ['light', 'white', 'grayscale', 'dark', 'black'];
|
|
26870
|
+
|
|
26871
|
+
// ------------------------------------------------------------------ basemap classes (contract §1)
|
|
26872
|
+
|
|
26873
|
+
// shared/basemap-classes.json, `paint` and `classes` as [layer, role] pairs (test/source-interop.test.cjs checks
|
|
26874
|
+
// that it equals the shared file).
|
|
26875
|
+
var BASEMAP = {"paint":{"background":{"color":"background-color","opacity":"background-opacity"},"fill":{"color":"fill-color","opacity":"fill-opacity"},"line":{"color":"line-color","opacity":"line-opacity","width":"line-width"},"casing":{"casing":"line-color","opacity":"line-opacity","width":"line-width"},"label":{"color":"text-color","halo":"text-halo-color","opacity":"text-opacity"}},"classes":{"land":[["background","background"],["earth","fill"]],"landcover":[["landcover","fill"]],"parks":[["landuse_park","fill"],["landuse_urban_green","fill"]],"landuse":[["landuse_hospital","fill"],["landuse_industrial","fill"],["landuse_school","fill"],["landuse_beach","fill"],["landuse_zoo","fill"],["landuse_pedestrian","fill"],["landuse_pier","fill"]],"airports":[["landuse_aerodrome","fill"],["roads_runway","line"],["roads_taxiway","line"],["landuse_runway","fill"]],"water":[["water","fill"],["water_stream","line"],["water_river","line"]],"roads.paths":[["roads_tunnels_other_casing","casing"],["roads_tunnels_other","line"],["roads_pier","line"],["roads_other","line"],["roads_bridges_other_casing","casing"],["roads_bridges_other","line"]],"roads.minor":[["roads_tunnels_minor_casing","casing"],["roads_tunnels_link_casing","casing"],["roads_tunnels_minor","line"],["roads_tunnels_link","line"],["roads_minor_service_casing","casing"],["roads_minor_casing","casing"],["roads_link_casing","casing"],["roads_link","line"],["roads_minor_service","line"],["roads_minor","line"],["roads_bridges_link_casing","casing"],["roads_bridges_minor_casing","casing"],["roads_bridges_minor","line"],["roads_bridges_link","line"]],"roads.major":[["roads_tunnels_major_casing","casing"],["roads_tunnels_major","line"],["roads_major_casing_late","casing"],["roads_major_casing_early","casing"],["roads_major","line"],["roads_bridges_major_casing","casing"],["roads_bridges_major","line"]],"roads.highway":[["roads_tunnels_highway_casing","casing"],["roads_tunnels_highway","line"],["roads_highway_casing_late","casing"],["roads_highway_casing_early","casing"],["roads_highway","line"],["roads_bridges_highway_casing","casing"],["roads_bridges_highway","line"]],"buildings":[["buildings","fill"]],"rail":[["roads_rail","line"]],"boundaries":[["boundaries_country","line"],["boundaries","line"]],"labels.addresses":[["address_label","label"]],"labels.water":[["water_waterway_label","label"],["water_label_ocean","label"],["water_label_lakes","label"]],"labels.roads":[["roads_oneway","label"],["roads_labels_minor","label"],["roads_shields","label"],["roads_labels_major","label"]],"labels.places":[["earth_label_islands","label"],["places_subplace","label"],["places_region","label"],["places_locality","label"],["places_country","label"]],"pois":[["pois","label"]]}};
|
|
26876
|
+
var OVERRIDE_FIELDS = ['visible', 'color', 'casing', 'halo', 'opacity', 'width'];
|
|
26877
|
+
var PARENTS = ['roads', 'labels'];
|
|
26878
|
+
|
|
26879
|
+
/** The class table as the shared file has it: {paint, classes: {name: [{layer, role}]}}. */
|
|
26880
|
+
function basemapClasses() {
|
|
26881
|
+
var classes = {};
|
|
26882
|
+
Object.keys(BASEMAP.classes).forEach(function (name) {
|
|
26883
|
+
classes[name] = BASEMAP.classes[name].map(function (p) { return { layer: p[0], role: p[1] }; });
|
|
26884
|
+
});
|
|
26885
|
+
return { paint: JSON.parse(JSON.stringify(BASEMAP.paint)), classes: classes };
|
|
26886
|
+
}
|
|
26887
|
+
|
|
26888
|
+
// A table given as the shared file ({classes: {name: [{layer, role}]}}) or as the embedded one.
|
|
26889
|
+
function classTable(table) {
|
|
26890
|
+
if (!table) { return BASEMAP; }
|
|
26891
|
+
var classes = {};
|
|
26892
|
+
Object.keys(table.classes || {}).forEach(function (name) {
|
|
26893
|
+
classes[name] = (table.classes[name] || []).map(function (p) { return Array.isArray(p) ? p : [p.layer, p.role]; });
|
|
26894
|
+
});
|
|
26895
|
+
return { paint: table.paint || BASEMAP.paint, classes: classes };
|
|
26896
|
+
}
|
|
26897
|
+
|
|
26898
|
+
function isColor(v) { return typeof v === 'string' && v.length > 0; }
|
|
26899
|
+
|
|
26900
|
+
/**
|
|
26901
|
+
* Checks basemap overrides (contract §1) and returns the list of [class, override] in the order they apply:
|
|
26902
|
+
* parents (`roads`, `labels`) first, then the classes in the order given. An unknown class or field, or a value of
|
|
26903
|
+
* the wrong kind, throws an Error naming it.
|
|
26904
|
+
*/
|
|
26905
|
+
function overrideOrder(overrides, table) {
|
|
26906
|
+
var t = classTable(table);
|
|
26907
|
+
if (overrides === null || overrides === undefined) { return []; }
|
|
26908
|
+
if (typeof overrides !== 'object' || Array.isArray(overrides)) { throw new Error('sMap: basemap overrides must be an object'); }
|
|
26909
|
+
var parents = [], children = [];
|
|
26910
|
+
Object.keys(overrides).forEach(function (name) {
|
|
26911
|
+
var isParent = PARENTS.indexOf(name) >= 0;
|
|
26912
|
+
if (!isParent && !t.classes[name]) { throw new Error('sMap: unknown basemap class "' + name + '"'); }
|
|
26913
|
+
var o = overrides[name];
|
|
26914
|
+
if (!o || typeof o !== 'object' || Array.isArray(o)) { throw new Error('sMap: basemap class "' + name + '" needs an object'); }
|
|
26915
|
+
Object.keys(o).forEach(function (field) {
|
|
26916
|
+
var v = o[field];
|
|
26917
|
+
if (OVERRIDE_FIELDS.indexOf(field) < 0) { throw new Error('sMap: unknown field "' + field + '" on basemap class "' + name + '"'); }
|
|
26918
|
+
if (field === 'visible' && typeof v !== 'boolean') { throw new Error('sMap: "' + name + '.visible" must be true or false'); }
|
|
26919
|
+
if ((field === 'color' || field === 'casing' || field === 'halo') && !isColor(v)) { throw new Error('sMap: "' + name + '.' + field + '" must be a colour'); }
|
|
26920
|
+
if (field === 'opacity' && !(typeof v === 'number' && v >= 0 && v <= 1)) { throw new Error('sMap: "' + name + '.opacity" must be between 0 and 1'); }
|
|
26921
|
+
if (field === 'width' && !(typeof v === 'number' && v >= 0.25 && v <= 4)) { throw new Error('sMap: "' + name + '.width" must be between 0.25 and 4'); }
|
|
26922
|
+
});
|
|
26923
|
+
(isParent ? parents : children).push(name);
|
|
26924
|
+
});
|
|
26925
|
+
var out = [];
|
|
26926
|
+
parents.sort(function (a, b) { return PARENTS.indexOf(a) - PARENTS.indexOf(b); });
|
|
26927
|
+
parents.forEach(function (p) {
|
|
26928
|
+
Object.keys(t.classes).forEach(function (name) { if (name.indexOf(p + '.') === 0) { out.push([name, overrides[p]]); } });
|
|
26929
|
+
});
|
|
26930
|
+
children.forEach(function (name) { out.push([name, overrides[name]]); });
|
|
26931
|
+
return out;
|
|
26932
|
+
}
|
|
26933
|
+
|
|
26934
|
+
function round4(n) { return Math.round(n * 10000) / 10000; }
|
|
26935
|
+
|
|
26936
|
+
// line-width × factor (contract §10): a number is multiplied (rounded to 4 decimals); a top-level interpolate /
|
|
26937
|
+
// step (or legacy {stops}) keeps its structure with each output multiplied the same way, since the engine accepts
|
|
26938
|
+
// a zoom expression only at the top of a value; anything else becomes ["*", factor, <original>]. Superroute's
|
|
26939
|
+
// preview (SmapBasemapClasses::scaleWidth) gives the same values.
|
|
26940
|
+
function scaledWidth(width, factor) {
|
|
26941
|
+
if (typeof width === 'number') { return round4(width * factor); }
|
|
26942
|
+
var k;
|
|
26943
|
+
if (width && typeof width === 'object' && !Array.isArray(width) && Array.isArray(width.stops)) {
|
|
26944
|
+
width = JSON.parse(JSON.stringify(width));
|
|
26945
|
+
for (k = 0; k < width.stops.length; k++) { if (Array.isArray(width.stops[k]) && width.stops[k].length === 2) { width.stops[k][1] = scaledWidth(width.stops[k][1], factor); } }
|
|
26946
|
+
return width;
|
|
26947
|
+
}
|
|
26948
|
+
if (Array.isArray(width) && width[0] === 'interpolate') {
|
|
26949
|
+
width = width.slice();
|
|
26950
|
+
for (k = 4; k < width.length; k += 2) { width[k] = scaledWidth(width[k], factor); }
|
|
26951
|
+
return width;
|
|
26952
|
+
}
|
|
26953
|
+
if (Array.isArray(width) && width[0] === 'step') {
|
|
26954
|
+
width = width.slice();
|
|
26955
|
+
for (k = 2; k < width.length; k += 2) { width[k] = scaledWidth(width[k], factor); }
|
|
26956
|
+
return width;
|
|
26957
|
+
}
|
|
26958
|
+
return ['*', factor, width];
|
|
26959
|
+
}
|
|
26960
|
+
|
|
26961
|
+
// Applies one class's fields to one style layer of a role, in place. The width always scales the scheme's own
|
|
26962
|
+
// width: the fields are merged (parent, then the class) before anything is applied.
|
|
26963
|
+
function applyToLayer(layer, role, o, paintKeys) {
|
|
26964
|
+
var keys = paintKeys[role] || {};
|
|
26965
|
+
if (o.visible !== undefined) { layer.layout = layer.layout || {}; layer.layout.visibility = o.visible ? 'visible' : 'none'; }
|
|
26966
|
+
var paint = function (k, v) { if (k) { layer.paint = layer.paint || {}; layer.paint[k] = v; } };
|
|
26967
|
+
if (o.color !== undefined && role !== 'casing') { paint(keys.color, o.color); }
|
|
26968
|
+
if (o.casing !== undefined && role === 'casing') { paint(keys.casing, o.casing); }
|
|
26969
|
+
if (o.halo !== undefined && role === 'label') { paint(keys.halo, o.halo); }
|
|
26970
|
+
if (o.opacity !== undefined) { paint(keys.opacity, o.opacity); }
|
|
26971
|
+
if (o.width !== undefined && keys.width) {
|
|
26972
|
+
var w = layer.paint ? layer.paint[keys.width] : undefined;
|
|
26973
|
+
paint(keys.width, scaledWidth(w === undefined ? 1 : w, o.width));
|
|
26974
|
+
}
|
|
26975
|
+
}
|
|
26976
|
+
|
|
26977
|
+
/** The fields that apply to each class: its parent's (roads, labels), then its own, which win. */
|
|
26978
|
+
function effectiveOverrides(overrides, table) {
|
|
26979
|
+
var t = classTable(table);
|
|
26980
|
+
overrideOrder(overrides, table);
|
|
26981
|
+
var out = {};
|
|
26982
|
+
if (!overrides) { return out; }
|
|
26983
|
+
Object.keys(t.classes).forEach(function (name) {
|
|
26984
|
+
var dot = name.indexOf('.');
|
|
26985
|
+
var merged = {}, any = false;
|
|
26986
|
+
var parent = dot > 0 ? overrides[name.slice(0, dot)] : null;
|
|
26987
|
+
[parent, overrides[name]].forEach(function (o) { if (o) { Object.keys(o).forEach(function (f) { merged[f] = o[f]; any = true; }); } });
|
|
26988
|
+
if (any) { out[name] = merged; }
|
|
26989
|
+
});
|
|
26990
|
+
return out;
|
|
26991
|
+
}
|
|
26992
|
+
|
|
26993
|
+
/**
|
|
26994
|
+
* The style with basemap overrides applied (contract §1 and §10): a copy, the input is never changed. `table` is
|
|
26995
|
+
* the class table (default: the one bundled, equal to shared/basemap-classes.json). Layers of the table the style
|
|
26996
|
+
* lacks are skipped; an unknown class or field throws.
|
|
26997
|
+
*/
|
|
26998
|
+
function applyBasemapOverrides(style, overrides, table) {
|
|
26999
|
+
var t = classTable(table);
|
|
27000
|
+
var eff = effectiveOverrides(overrides, table);
|
|
27001
|
+
var out = JSON.parse(JSON.stringify(style || {}));
|
|
27002
|
+
if (!Object.keys(eff).length || !Array.isArray(out.layers)) { return out; }
|
|
27003
|
+
var byId = {};
|
|
27004
|
+
out.layers.forEach(function (l) { byId[l.id] = l; });
|
|
27005
|
+
Object.keys(eff).forEach(function (name) {
|
|
27006
|
+
t.classes[name].forEach(function (lr) {
|
|
27007
|
+
var layer = byId[lr[0]];
|
|
27008
|
+
if (layer) { applyToLayer(layer, lr[1], eff[name], t.paint); }
|
|
27009
|
+
});
|
|
27010
|
+
});
|
|
27011
|
+
return out;
|
|
27012
|
+
}
|
|
27013
|
+
|
|
27014
|
+
/** layer id → {class, role} for every basemap layer of the table. */
|
|
27015
|
+
function basemapLayerIndex(table) {
|
|
27016
|
+
var t = classTable(table), idx = {};
|
|
27017
|
+
Object.keys(t.classes).forEach(function (name) {
|
|
27018
|
+
t.classes[name].forEach(function (lr) { idx[lr[0]] = { 'class': name, role: lr[1] }; });
|
|
27019
|
+
});
|
|
27020
|
+
return idx;
|
|
27021
|
+
}
|
|
27022
|
+
|
|
27023
|
+
// ------------------------------------------------------------------ slots (M25)
|
|
27024
|
+
|
|
27025
|
+
var SLOTS = ['bottom', 'roads', 'labels', 'top'];
|
|
27026
|
+
|
|
27027
|
+
/**
|
|
27028
|
+
* Where each slot begins in a basemap's layer list (ids bottom to top): the id of the basemap layer a layer of that
|
|
27029
|
+
* slot is drawn under, or undefined for the very top.
|
|
27030
|
+
* labels: the first label layer (classes labels.* and pois)
|
|
27031
|
+
* roads: the first road layer (classes roads.* and rail), else labels
|
|
27032
|
+
* bottom: the layer after the last layer of land, landcover and water, else roads (never above roads)
|
|
27033
|
+
* top: undefined
|
|
27034
|
+
* Stable when the basemap's list changes: it is found from the classes, never from a position.
|
|
27035
|
+
*/
|
|
27036
|
+
function slotAnchors(layerIds, table) {
|
|
27037
|
+
var idx = basemapLayerIndex(table);
|
|
27038
|
+
var cls = function (id) { return idx[id] && idx[id]['class']; };
|
|
27039
|
+
var first = function (test) { for (var i = 0; i < layerIds.length; i++) { var c = cls(layerIds[i]); if (c && test(c)) { return i; } } return -1; };
|
|
27040
|
+
var labels = first(function (c) { return c.indexOf('labels.') === 0 || c === 'pois'; });
|
|
27041
|
+
var roads = first(function (c) { return c.indexOf('roads.') === 0 || c === 'rail'; });
|
|
27042
|
+
if (roads < 0 || (labels >= 0 && roads > labels)) { roads = labels; }
|
|
27043
|
+
var lastGround = -1;
|
|
27044
|
+
for (var i = 0; i < layerIds.length; i++) { var c = cls(layerIds[i]); if (c === 'land' || c === 'landcover' || c === 'water') { lastGround = i; } }
|
|
27045
|
+
var bottom = lastGround >= 0 && lastGround + 1 < layerIds.length ? lastGround + 1 : roads;
|
|
27046
|
+
if (roads >= 0 && (bottom < 0 || bottom > roads)) { bottom = roads; }
|
|
27047
|
+
var id = function (i) { return i >= 0 ? layerIds[i] : undefined; };
|
|
27048
|
+
return { bottom: id(bottom), roads: id(roads), labels: id(labels), top: undefined };
|
|
27049
|
+
}
|
|
27050
|
+
|
|
27051
|
+
// ------------------------------------------------------------------ bytes, text, inflate
|
|
27052
|
+
|
|
27053
|
+
function toBytes(input) {
|
|
27054
|
+
if (input instanceof Uint8Array) { return input; }
|
|
27055
|
+
if (typeof ArrayBuffer !== 'undefined' && input instanceof ArrayBuffer) { return new Uint8Array(input); }
|
|
27056
|
+
if (input && input.buffer instanceof ArrayBuffer && typeof input.byteLength === 'number') { return new Uint8Array(input.buffer, input.byteOffset || 0, input.byteLength); }
|
|
27057
|
+
if (input && typeof input.length === 'number' && typeof input !== 'string') { return Uint8Array.from(input); }
|
|
27058
|
+
return null;
|
|
27059
|
+
}
|
|
27060
|
+
|
|
27061
|
+
function utf8(bytes) {
|
|
27062
|
+
if (typeof TextDecoder !== 'undefined') { return new TextDecoder('utf-8').decode(bytes); }
|
|
27063
|
+
var s = '';
|
|
27064
|
+
for (var i = 0; i < bytes.length; i++) { s += String.fromCharCode(bytes[i]); }
|
|
27065
|
+
return decodeURIComponent(escape(s));
|
|
27066
|
+
}
|
|
27067
|
+
|
|
27068
|
+
function stripBom(s) { return s.charCodeAt(0) === 0xfeff ? s.slice(1) : s; }
|
|
27069
|
+
|
|
27070
|
+
var LBASE = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];
|
|
27071
|
+
var LEXT = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
|
|
27072
|
+
var DBASE = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577];
|
|
27073
|
+
var DEXT = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13];
|
|
27074
|
+
var CLORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
|
27075
|
+
|
|
27076
|
+
function huffman(lengths) {
|
|
27077
|
+
var count = new Uint16Array(16), offs = new Uint16Array(16), symbol = new Uint16Array(lengths.length);
|
|
27078
|
+
for (var s = 0; s < lengths.length; s++) { count[lengths[s]]++; }
|
|
27079
|
+
count[0] = 0;
|
|
27080
|
+
for (var len = 1; len < 15; len++) { offs[len + 1] = offs[len] + count[len]; }
|
|
27081
|
+
for (s = 0; s < lengths.length; s++) { if (lengths[s]) { symbol[offs[lengths[s]]++] = s; } }
|
|
27082
|
+
return { count: count, symbol: symbol };
|
|
27083
|
+
}
|
|
27084
|
+
|
|
27085
|
+
/** Raw DEFLATE (RFC 1951) → bytes. Small and synchronous, for KMZ, gzip and PMTiles directories. */
|
|
27086
|
+
function inflateRaw(src) {
|
|
27087
|
+
src = toBytes(src);
|
|
27088
|
+
var pos = 0, bitBuf = 0, bitCnt = 0, out = new Uint8Array(Math.max(1024, src.length * 4)), n = 0;
|
|
27089
|
+
function need(k) { if (n + k > out.length) { var b = new Uint8Array(Math.max(out.length * 2, n + k)); b.set(out.subarray(0, n)); out = b; } }
|
|
27090
|
+
function bits(k) {
|
|
27091
|
+
while (bitCnt < k) { if (pos >= src.length) { throw new Error('sMap: compressed data ends early'); } bitBuf |= src[pos++] << bitCnt; bitCnt += 8; }
|
|
27092
|
+
var v = bitBuf & ((1 << k) - 1);
|
|
27093
|
+
bitBuf >>>= k; bitCnt -= k;
|
|
27094
|
+
return v;
|
|
27095
|
+
}
|
|
27096
|
+
function decode(h) {
|
|
27097
|
+
var code = 0, first = 0, index = 0;
|
|
27098
|
+
for (var len = 1; len < 16; len++) {
|
|
27099
|
+
code |= bits(1);
|
|
27100
|
+
var c = h.count[len];
|
|
27101
|
+
if (code - first < c) { return h.symbol[index + code - first]; }
|
|
27102
|
+
index += c; first += c; first <<= 1; code <<= 1;
|
|
27103
|
+
}
|
|
27104
|
+
throw new Error('sMap: bad compressed data');
|
|
27105
|
+
}
|
|
27106
|
+
var fixedLit = null, fixedDist = null;
|
|
27107
|
+
var last;
|
|
27108
|
+
do {
|
|
27109
|
+
last = bits(1);
|
|
27110
|
+
var type = bits(2);
|
|
27111
|
+
if (type === 0) {
|
|
27112
|
+
bitBuf = 0; bitCnt = 0;
|
|
27113
|
+
if (pos + 4 > src.length) { throw new Error('sMap: compressed data ends early'); }
|
|
27114
|
+
var len = src[pos] | (src[pos + 1] << 8);
|
|
27115
|
+
pos += 4;
|
|
27116
|
+
if (pos + len > src.length) { throw new Error('sMap: compressed data ends early'); }
|
|
27117
|
+
need(len); out.set(src.subarray(pos, pos + len), n); n += len; pos += len;
|
|
27118
|
+
continue;
|
|
27119
|
+
}
|
|
27120
|
+
var lit, dist;
|
|
27121
|
+
if (type === 1) {
|
|
27122
|
+
if (!fixedLit) {
|
|
27123
|
+
var l = new Uint8Array(288);
|
|
27124
|
+
for (var i = 0; i < 288; i++) { l[i] = i < 144 ? 8 : i < 256 ? 9 : i < 280 ? 7 : 8; }
|
|
27125
|
+
fixedLit = huffman(l);
|
|
27126
|
+
var d = new Uint8Array(30); d.fill(5); fixedDist = huffman(d);
|
|
27127
|
+
}
|
|
27128
|
+
lit = fixedLit; dist = fixedDist;
|
|
27129
|
+
} else if (type === 2) {
|
|
27130
|
+
var hlit = bits(5) + 257, hdist = bits(5) + 1, hclen = bits(4) + 4;
|
|
27131
|
+
var cl = new Uint8Array(19);
|
|
27132
|
+
for (i = 0; i < hclen; i++) { cl[CLORDER[i]] = bits(3); }
|
|
27133
|
+
var clh = huffman(cl);
|
|
27134
|
+
var lengths = new Uint8Array(hlit + hdist);
|
|
27135
|
+
for (i = 0; i < hlit + hdist;) {
|
|
27136
|
+
var sym = decode(clh);
|
|
27137
|
+
if (sym < 16) { lengths[i++] = sym; continue; }
|
|
27138
|
+
var rep, val = 0;
|
|
27139
|
+
if (sym === 16) { if (i === 0) { throw new Error('sMap: bad compressed data'); } val = lengths[i - 1]; rep = 3 + bits(2); } else if (sym === 17) { rep = 3 + bits(3); } else { rep = 11 + bits(7); }
|
|
27140
|
+
if (i + rep > hlit + hdist) { throw new Error('sMap: bad compressed data'); }
|
|
27141
|
+
while (rep--) { lengths[i++] = val; }
|
|
27142
|
+
}
|
|
27143
|
+
lit = huffman(lengths.subarray(0, hlit)); dist = huffman(lengths.subarray(hlit));
|
|
27144
|
+
} else { throw new Error('sMap: bad compressed data'); }
|
|
27145
|
+
for (;;) {
|
|
27146
|
+
var s = decode(lit);
|
|
27147
|
+
if (s < 256) { need(1); out[n++] = s; continue; }
|
|
27148
|
+
if (s === 256) { break; }
|
|
27149
|
+
s -= 257;
|
|
27150
|
+
if (s >= 29) { throw new Error('sMap: bad compressed data'); }
|
|
27151
|
+
var length = LBASE[s] + bits(LEXT[s]);
|
|
27152
|
+
var ds = decode(dist);
|
|
27153
|
+
if (ds >= 30) { throw new Error('sMap: bad compressed data'); }
|
|
27154
|
+
var back = DBASE[ds] + bits(DEXT[ds]);
|
|
27155
|
+
if (back > n) { throw new Error('sMap: bad compressed data'); }
|
|
27156
|
+
need(length);
|
|
27157
|
+
for (var k = 0; k < length; k++) { out[n] = out[n - back]; n++; }
|
|
27158
|
+
}
|
|
27159
|
+
} while (!last);
|
|
27160
|
+
return out.slice(0, n);
|
|
27161
|
+
}
|
|
27162
|
+
|
|
27163
|
+
/** gzip (RFC 1952) → bytes; bytes that are not gzip are returned as they are. */
|
|
27164
|
+
function gunzip(bytes) {
|
|
27165
|
+
bytes = toBytes(bytes);
|
|
27166
|
+
if (bytes.length < 18 || bytes[0] !== 0x1f || bytes[1] !== 0x8b) { return bytes; }
|
|
27167
|
+
if (bytes[2] !== 8) { throw new Error('sMap: unsupported gzip method'); }
|
|
27168
|
+
var flags = bytes[3], p = 10;
|
|
27169
|
+
if (flags & 4) { p += 2 + (bytes[p] | (bytes[p + 1] << 8)); }
|
|
27170
|
+
if (flags & 8) { while (bytes[p++] !== 0) { /* name */ } }
|
|
27171
|
+
if (flags & 16) { while (bytes[p++] !== 0) { /* comment */ } }
|
|
27172
|
+
if (flags & 2) { p += 2; }
|
|
27173
|
+
return inflateRaw(bytes.subarray(p));
|
|
27174
|
+
}
|
|
27175
|
+
|
|
27176
|
+
/** The files of a ZIP archive: [{name, bytes()}] (stored and deflated entries). */
|
|
27177
|
+
function unzip(bytes) {
|
|
27178
|
+
bytes = toBytes(bytes);
|
|
27179
|
+
var dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
27180
|
+
var e = -1;
|
|
27181
|
+
for (var i = bytes.length - 22; i >= Math.max(0, bytes.length - 65557); i--) { if (dv.getUint32(i, true) === 0x06054b50) { e = i; break; } }
|
|
27182
|
+
if (e < 0) { throw new Error('sMap: not a ZIP archive'); }
|
|
27183
|
+
var count = dv.getUint16(e + 10, true), p = dv.getUint32(e + 16, true), files = [];
|
|
27184
|
+
for (var k = 0; k < count; k++) {
|
|
27185
|
+
if (dv.getUint32(p, true) !== 0x02014b50) { throw new Error('sMap: bad ZIP directory'); }
|
|
27186
|
+
var method = dv.getUint16(p + 10, true), size = dv.getUint32(p + 20, true);
|
|
27187
|
+
var nameLen = dv.getUint16(p + 28, true), extraLen = dv.getUint16(p + 30, true), commentLen = dv.getUint16(p + 32, true);
|
|
27188
|
+
var local = dv.getUint32(p + 42, true);
|
|
27189
|
+
var name = utf8(bytes.subarray(p + 46, p + 46 + nameLen));
|
|
27190
|
+
p += 46 + nameLen + extraLen + commentLen;
|
|
27191
|
+
(function (method, size, local, name) {
|
|
27192
|
+
files.push({
|
|
27193
|
+
name: name,
|
|
27194
|
+
bytes: function () {
|
|
27195
|
+
var start = local + 30 + dv.getUint16(local + 26, true) + dv.getUint16(local + 28, true);
|
|
27196
|
+
var raw = bytes.subarray(start, start + size);
|
|
27197
|
+
if (method === 0) { return raw; }
|
|
27198
|
+
if (method === 8) { return inflateRaw(raw); }
|
|
27199
|
+
throw new Error('sMap: unsupported ZIP method ' + method + ' for ' + name);
|
|
27200
|
+
},
|
|
27201
|
+
});
|
|
27202
|
+
})(method, size, local, name);
|
|
27203
|
+
}
|
|
27204
|
+
return files;
|
|
27205
|
+
}
|
|
27206
|
+
|
|
27207
|
+
// ------------------------------------------------------------------ XML (enough for KML, GPX, WMTS capabilities)
|
|
27208
|
+
|
|
27209
|
+
var ENTITIES = { lt: '<', gt: '>', amp: '&', quot: '"', apos: "'" };
|
|
27210
|
+
function decodeEntities(s) {
|
|
27211
|
+
return s.indexOf('&') < 0 ? s : s.replace(/&(#x[0-9a-fA-F]+|#[0-9]+|[a-zA-Z]+);/g, function (m, e) {
|
|
27212
|
+
if (e[0] === '#') { var cp = e[1] === 'x' || e[1] === 'X' ? parseInt(e.slice(2), 16) : parseInt(e.slice(1), 10); return isFinite(cp) ? String.fromCodePoint(cp) : m; }
|
|
27213
|
+
return ENTITIES[e] !== undefined ? ENTITIES[e] : m;
|
|
27214
|
+
});
|
|
27215
|
+
}
|
|
27216
|
+
|
|
27217
|
+
/** A small XML reader: elements {name, local, attrs, children} where children holds elements and strings. */
|
|
27218
|
+
function parseXml(s) {
|
|
27219
|
+
var doc = { name: '#document', local: '#document', attrs: {}, children: [] }, stack = [doc], i = 0, n = s.length;
|
|
27220
|
+
var top = function () { return stack[stack.length - 1]; };
|
|
27221
|
+
while (i < n) {
|
|
27222
|
+
var lt = s.indexOf('<', i);
|
|
27223
|
+
if (lt < 0) { top().children.push(decodeEntities(s.slice(i))); break; }
|
|
27224
|
+
if (lt > i) { top().children.push(decodeEntities(s.slice(i, lt))); }
|
|
27225
|
+
var end;
|
|
27226
|
+
if (s.startsWith('<!--', lt)) { end = s.indexOf('-->', lt + 4); i = end < 0 ? n : end + 3; continue; }
|
|
27227
|
+
if (s.startsWith('<![CDATA[', lt)) { end = s.indexOf(']]>', lt + 9); top().children.push(s.slice(lt + 9, end < 0 ? n : end)); i = end < 0 ? n : end + 3; continue; }
|
|
27228
|
+
if (s[lt + 1] === '?') { end = s.indexOf('?>', lt + 2); i = end < 0 ? n : end + 2; continue; }
|
|
27229
|
+
if (s[lt + 1] === '!') {
|
|
27230
|
+
var depth = 0, j = lt + 2;
|
|
27231
|
+
for (; j < n; j++) { if (s[j] === '[') { depth++; } else if (s[j] === ']') { depth--; } else if (s[j] === '>' && depth <= 0) { break; } }
|
|
27232
|
+
i = j + 1; continue;
|
|
27233
|
+
}
|
|
27234
|
+
if (s[lt + 1] === '/') {
|
|
27235
|
+
end = s.indexOf('>', lt);
|
|
27236
|
+
var closing = s.slice(lt + 2, end < 0 ? n : end).trim();
|
|
27237
|
+
for (var k = stack.length - 1; k > 0; k--) { if (stack[k].name === closing) { stack.length = k; break; } }
|
|
27238
|
+
i = end < 0 ? n : end + 1; continue;
|
|
27239
|
+
}
|
|
27240
|
+
var q = null, p = lt + 1;
|
|
27241
|
+
for (; p < n; p++) { var c = s[p]; if (q) { if (c === q) { q = null; } } else if (c === '"' || c === "'") { q = c; } else if (c === '>') { break; } }
|
|
27242
|
+
var body = s.slice(lt + 1, p), selfClose = /\/\s*$/.test(body);
|
|
27243
|
+
if (selfClose) { body = body.replace(/\/\s*$/, ''); }
|
|
27244
|
+
var m = /^([^\s/>]+)/.exec(body);
|
|
27245
|
+
if (!m) { i = p + 1; continue; }
|
|
27246
|
+
var attrs = {}, re = /([^\s=]+)\s*=\s*("([^"]*)"|'([^']*)')/g, a;
|
|
27247
|
+
var rest = body.slice(m[1].length);
|
|
27248
|
+
while ((a = re.exec(rest))) { attrs[a[1]] = decodeEntities(a[3] !== undefined ? a[3] : a[4]); }
|
|
27249
|
+
var el = { name: m[1], local: m[1].replace(/^.*:/, ''), attrs: attrs, children: [] };
|
|
27250
|
+
top().children.push(el);
|
|
27251
|
+
if (!selfClose) { stack.push(el); }
|
|
27252
|
+
i = p + 1;
|
|
27253
|
+
}
|
|
27254
|
+
return doc;
|
|
27255
|
+
}
|
|
27256
|
+
|
|
27257
|
+
function elems(el, local) { var out = []; if (el) { el.children.forEach(function (c) { if (typeof c !== 'string' && (!local || c.local === local)) { out.push(c); } }); } return out; }
|
|
27258
|
+
function elem(el, local) { return elems(el, local)[0] || null; }
|
|
27259
|
+
function textOf(el) {
|
|
27260
|
+
if (!el) { return ''; }
|
|
27261
|
+
var s = '';
|
|
27262
|
+
el.children.forEach(function (c) { s += typeof c === 'string' ? c : textOf(c); });
|
|
27263
|
+
return s;
|
|
27264
|
+
}
|
|
27265
|
+
function trimmed(el) { return textOf(el).trim(); }
|
|
27266
|
+
function descendants(el, local, out) {
|
|
27267
|
+
out = out || [];
|
|
27268
|
+
elems(el).forEach(function (c) { if (c.local === local) { out.push(c); } descendants(c, local, out); });
|
|
27269
|
+
return out;
|
|
27270
|
+
}
|
|
27271
|
+
function attr(el, local) {
|
|
27272
|
+
if (!el) { return undefined; }
|
|
27273
|
+
for (var k in el.attrs) { if (k === local || k.replace(/^.*:/, '') === local) { return el.attrs[k]; } }
|
|
27274
|
+
return undefined;
|
|
27275
|
+
}
|
|
27276
|
+
|
|
27277
|
+
// ------------------------------------------------------------------ formats: read
|
|
27278
|
+
|
|
27279
|
+
var FORMATS = ['kml', 'kmz', 'gpx', 'polyline', 'polyline6', 'wkt', 'wkb', 'csv', 'topojson', 'geojson'];
|
|
27280
|
+
var GEOMETRY_TYPES = ['Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon', 'GeometryCollection'];
|
|
27281
|
+
|
|
27282
|
+
function fc(features) { return { type: 'FeatureCollection', features: features }; }
|
|
27283
|
+
function feature(properties, geometry, id) {
|
|
27284
|
+
var f = { type: 'Feature' };
|
|
27285
|
+
if (id !== undefined && id !== null) { f.id = id; }
|
|
27286
|
+
f.properties = properties || {};
|
|
27287
|
+
f.geometry = geometry || null;
|
|
27288
|
+
return f;
|
|
27289
|
+
}
|
|
27290
|
+
|
|
27291
|
+
// KML colour aabbggrr → ['#rrggbb', opacity rounded to 2 decimals]
|
|
27292
|
+
function kmlColor(v) {
|
|
27293
|
+
v = String(v || '').trim().replace(/^#/, '');
|
|
27294
|
+
if (!/^[0-9a-fA-F]{8}$/.test(v)) { return null; }
|
|
27295
|
+
v = v.toLowerCase();
|
|
27296
|
+
return ['#' + v.slice(6, 8) + v.slice(4, 6) + v.slice(2, 4), Math.round(parseInt(v.slice(0, 2), 16) / 255 * 100) / 100];
|
|
27297
|
+
}
|
|
27298
|
+
|
|
27299
|
+
// Tuples are separated by white space that is not next to a comma ("-79.38, 43.65" is one tuple).
|
|
27300
|
+
function kmlCoords(text) {
|
|
27301
|
+
var out = [];
|
|
27302
|
+
String(text).replace(/\s*,\s*/g, ',').trim().split(/\s+/).forEach(function (tuple) {
|
|
27303
|
+
if (!tuple) { return; }
|
|
27304
|
+
var p = tuple.split(',').map(Number);
|
|
27305
|
+
if (p.length < 2 || !isFinite(p[0]) || !isFinite(p[1])) { return; }
|
|
27306
|
+
out.push(p.length >= 3 && isFinite(p[2]) ? [p[0], p[1], p[2]] : [p[0], p[1]]);
|
|
27307
|
+
});
|
|
27308
|
+
return out;
|
|
27309
|
+
}
|
|
27310
|
+
|
|
27311
|
+
function kmlGeometry(el) {
|
|
27312
|
+
switch (el.local) {
|
|
27313
|
+
case 'Point': { var c = kmlCoords(trimmed(elem(el, 'coordinates'))); return c.length ? { type: 'Point', coordinates: c[0] } : null; }
|
|
27314
|
+
case 'LineString': case 'LinearRing': { var l = kmlCoords(trimmed(elem(el, 'coordinates'))); return l.length ? { type: 'LineString', coordinates: l } : null; }
|
|
27315
|
+
case 'Polygon': {
|
|
27316
|
+
var rings = [];
|
|
27317
|
+
var ring = function (b) { var r = elem(b, 'LinearRing'); var cs = r ? kmlCoords(trimmed(elem(r, 'coordinates'))) : []; if (cs.length) { rings.push(cs); } };
|
|
27318
|
+
elems(el, 'outerBoundaryIs').forEach(ring);
|
|
27319
|
+
if (!rings.length) { return null; }
|
|
27320
|
+
elems(el, 'innerBoundaryIs').forEach(ring);
|
|
27321
|
+
return { type: 'Polygon', coordinates: rings };
|
|
27322
|
+
}
|
|
27323
|
+
case 'Track': {
|
|
27324
|
+
var t = elems(el, 'coord').map(function (x) { var p = trimmed(x).split(/\s+/).map(Number); return p.length >= 3 && isFinite(p[2]) ? [p[0], p[1], p[2]] : [p[0], p[1]]; })
|
|
27325
|
+
.filter(function (p) { return isFinite(p[0]) && isFinite(p[1]); });
|
|
27326
|
+
return t.length ? { type: 'LineString', coordinates: t } : null;
|
|
27327
|
+
}
|
|
27328
|
+
case 'MultiTrack': {
|
|
27329
|
+
var lines = elems(el, 'Track').map(kmlGeometry).filter(Boolean).map(function (g) { return g.coordinates; });
|
|
27330
|
+
return lines.length ? { type: 'MultiLineString', coordinates: lines } : null;
|
|
27331
|
+
}
|
|
27332
|
+
case 'MultiGeometry': {
|
|
27333
|
+
var parts = elems(el).map(function (c) { return GEOMETRY_ELEMENTS.indexOf(c.local) >= 0 ? kmlGeometry(c) : null; }).filter(Boolean);
|
|
27334
|
+
return combine(parts);
|
|
27335
|
+
}
|
|
27336
|
+
}
|
|
27337
|
+
return null;
|
|
27338
|
+
}
|
|
27339
|
+
var GEOMETRY_ELEMENTS = ['Point', 'LineString', 'LinearRing', 'Polygon', 'MultiGeometry', 'Track', 'MultiTrack'];
|
|
27340
|
+
|
|
27341
|
+
// Parts of one feature: one part is itself; parts all Point / LineString / Polygon → Multi*; anything else a collection.
|
|
27342
|
+
function combine(parts) {
|
|
27343
|
+
if (!parts.length) { return null; }
|
|
27344
|
+
if (parts.length === 1) { return parts[0]; }
|
|
27345
|
+
var t = parts[0].type;
|
|
27346
|
+
var same = parts.every(function (g) { return g.type === t; });
|
|
27347
|
+
if (same && (t === 'Point' || t === 'LineString' || t === 'Polygon')) {
|
|
27348
|
+
return { type: 'Multi' + t, coordinates: parts.map(function (g) { return g.coordinates; }) };
|
|
27349
|
+
}
|
|
27350
|
+
return { type: 'GeometryCollection', geometries: parts };
|
|
27351
|
+
}
|
|
27352
|
+
|
|
27353
|
+
function kmlStyleProps(style) {
|
|
27354
|
+
var props = {};
|
|
27355
|
+
if (!style) { return props; }
|
|
27356
|
+
var icon = elem(style, 'IconStyle'), line = elem(style, 'LineStyle'), poly = elem(style, 'PolyStyle');
|
|
27357
|
+
var c;
|
|
27358
|
+
if (icon && (c = kmlColor(trimmed(elem(icon, 'color'))))) { props['marker-color'] = c[0]; }
|
|
27359
|
+
if (line) {
|
|
27360
|
+
if ((c = kmlColor(trimmed(elem(line, 'color'))))) { props.stroke = c[0]; props['stroke-opacity'] = c[1]; }
|
|
27361
|
+
var w = parseFloat(trimmed(elem(line, 'width')));
|
|
27362
|
+
if (isFinite(w)) { props['stroke-width'] = w; }
|
|
27363
|
+
}
|
|
27364
|
+
if (poly) {
|
|
27365
|
+
if ((c = kmlColor(trimmed(elem(poly, 'color'))))) { props.fill = c[0]; props['fill-opacity'] = c[1]; }
|
|
27366
|
+
if (trimmed(elem(poly, 'fill')) === '0') { props['fill-opacity'] = 0; }
|
|
27367
|
+
if (trimmed(elem(poly, 'outline')) === '0') { props['stroke-opacity'] = 0; }
|
|
27368
|
+
}
|
|
27369
|
+
return props;
|
|
27370
|
+
}
|
|
27371
|
+
|
|
27372
|
+
function readKml(text) {
|
|
27373
|
+
var doc = parseXml(stripBom(text));
|
|
27374
|
+
var styles = {}, maps = {};
|
|
27375
|
+
descendants(doc, 'Style').forEach(function (s) { if (s.attrs.id) { styles[s.attrs.id] = s; } });
|
|
27376
|
+
descendants(doc, 'StyleMap').forEach(function (s) {
|
|
27377
|
+
if (!s.attrs.id) { return; }
|
|
27378
|
+
elems(s, 'Pair').forEach(function (p) { if (trimmed(elem(p, 'key')) === 'normal') { maps[s.attrs.id] = trimmed(elem(p, 'styleUrl')); } });
|
|
27379
|
+
});
|
|
27380
|
+
var resolve = function (url, depth) {
|
|
27381
|
+
var id = String(url || '').replace(/^[^#]*#/, '');
|
|
27382
|
+
if (styles[id]) { return styles[id]; }
|
|
27383
|
+
if (maps[id] && depth < 4) { return resolve(maps[id], depth + 1); }
|
|
27384
|
+
return null;
|
|
27385
|
+
};
|
|
27386
|
+
var features = descendants(doc, 'Placemark').map(function (pm) {
|
|
27387
|
+
var props = {};
|
|
27388
|
+
var name = elem(pm, 'name'), desc = elem(pm, 'description');
|
|
27389
|
+
if (name) { props.name = trimmed(name); }
|
|
27390
|
+
if (desc) { props.description = trimmed(desc); }
|
|
27391
|
+
var ext = elem(pm, 'ExtendedData');
|
|
27392
|
+
if (ext) {
|
|
27393
|
+
elems(ext, 'Data').forEach(function (d) { if (d.attrs.name !== undefined) { props[d.attrs.name] = trimmed(elem(d, 'value')); } });
|
|
27394
|
+
elems(ext, 'SchemaData').forEach(function (sd) { elems(sd, 'SimpleData').forEach(function (d) { if (d.attrs.name !== undefined) { props[d.attrs.name] = trimmed(d); } }); });
|
|
27395
|
+
}
|
|
27396
|
+
var style = {};
|
|
27397
|
+
var url = elem(pm, 'styleUrl');
|
|
27398
|
+
if (url) { Object.assign(style, kmlStyleProps(resolve(trimmed(url), 0))); }
|
|
27399
|
+
var inline = elem(pm, 'Style');
|
|
27400
|
+
if (inline) { Object.assign(style, kmlStyleProps(inline)); }
|
|
27401
|
+
['marker-color', 'stroke', 'stroke-opacity', 'stroke-width', 'fill', 'fill-opacity'].forEach(function (k) { if (style[k] !== undefined) { props[k] = style[k]; } });
|
|
27402
|
+
var geomEl = elems(pm).filter(function (c) { return GEOMETRY_ELEMENTS.indexOf(c.local) >= 0; });
|
|
27403
|
+
return feature(props, combine(geomEl.map(kmlGeometry).filter(Boolean)));
|
|
27404
|
+
});
|
|
27405
|
+
return fc(features);
|
|
27406
|
+
}
|
|
27407
|
+
|
|
27408
|
+
function readKmz(bytes) {
|
|
27409
|
+
var files = unzip(bytes).filter(function (f) { return /\.kml$/i.test(f.name); });
|
|
27410
|
+
if (!files.length) { throw new Error('sMap: the KMZ file holds no KML document'); }
|
|
27411
|
+
var main = files.filter(function (f) { return /(^|\/)doc\.kml$/i.test(f.name); })[0]
|
|
27412
|
+
|| files.filter(function (f) { return f.name.indexOf('/') < 0; })[0] || files[0];
|
|
27413
|
+
return readKml(utf8(main.bytes()));
|
|
27414
|
+
}
|
|
27415
|
+
|
|
27416
|
+
var GPX_SKIP = ['ele', 'extensions', 'link', 'rtept', 'trkseg', 'trkpt'];
|
|
27417
|
+
function gpxProps(el) {
|
|
27418
|
+
var props = {};
|
|
27419
|
+
elems(el).forEach(function (c) {
|
|
27420
|
+
if (GPX_SKIP.indexOf(c.local) >= 0 || elems(c).length) { return; }
|
|
27421
|
+
props[c.local] = trimmed(c);
|
|
27422
|
+
});
|
|
27423
|
+
return props;
|
|
27424
|
+
}
|
|
27425
|
+
function gpxPoint(pt) {
|
|
27426
|
+
var lon = parseFloat(pt.attrs.lon), lat = parseFloat(pt.attrs.lat);
|
|
27427
|
+
if (!isFinite(lon) || !isFinite(lat)) { return null; }
|
|
27428
|
+
var ele = parseFloat(trimmed(elem(pt, 'ele')));
|
|
27429
|
+
return isFinite(ele) && elem(pt, 'ele') ? [lon, lat, ele] : [lon, lat];
|
|
27430
|
+
}
|
|
27431
|
+
|
|
27432
|
+
function readGpx(text) {
|
|
27433
|
+
var doc = parseXml(stripBom(text));
|
|
27434
|
+
var gpx = descendants(doc, 'gpx')[0];
|
|
27435
|
+
if (!gpx) { throw new Error('sMap: not a GPX document'); }
|
|
27436
|
+
var features = [];
|
|
27437
|
+
elems(gpx).forEach(function (el) {
|
|
27438
|
+
if (el.local === 'wpt') {
|
|
27439
|
+
var p = gpxPoint(el);
|
|
27440
|
+
if (p) { features.push(feature(gpxProps(el), { type: 'Point', coordinates: p })); }
|
|
27441
|
+
} else if (el.local === 'rte') {
|
|
27442
|
+
var r = elems(el, 'rtept').map(gpxPoint).filter(Boolean);
|
|
27443
|
+
if (r.length) { features.push(feature(gpxProps(el), { type: 'LineString', coordinates: r })); }
|
|
27444
|
+
} else if (el.local === 'trk') {
|
|
27445
|
+
var segs = elems(el, 'trkseg').map(function (s) { return elems(s, 'trkpt').map(gpxPoint).filter(Boolean); }).filter(function (s) { return s.length; });
|
|
27446
|
+
if (segs.length === 1) { features.push(feature(gpxProps(el), { type: 'LineString', coordinates: segs[0] })); }
|
|
27447
|
+
else if (segs.length > 1) { features.push(feature(gpxProps(el), { type: 'MultiLineString', coordinates: segs })); }
|
|
27448
|
+
}
|
|
27449
|
+
});
|
|
27450
|
+
return fc(features);
|
|
27451
|
+
}
|
|
27452
|
+
|
|
27453
|
+
/** Decodes an encoded polyline (precision 5: Google, OSRM; 6: Valhalla) to [[lng, lat], …]. */
|
|
27454
|
+
function decodePolyline(str, precision) {
|
|
27455
|
+
var factor = Math.pow(10, precision === undefined ? 5 : precision);
|
|
27456
|
+
str = String(str).trim();
|
|
27457
|
+
var i = 0, lat = 0, lng = 0, out = [];
|
|
27458
|
+
var next = function () {
|
|
27459
|
+
var result = 0, shift = 1, b;
|
|
27460
|
+
do {
|
|
27461
|
+
if (i >= str.length) { throw new Error('sMap: the polyline ends early'); }
|
|
27462
|
+
b = str.charCodeAt(i++) - 63;
|
|
27463
|
+
if (b < 0 || b > 63) { throw new Error('sMap: not an encoded polyline'); }
|
|
27464
|
+
result += (b & 0x1f) * shift;
|
|
27465
|
+
shift *= 32;
|
|
27466
|
+
} while (b >= 0x20);
|
|
27467
|
+
return result % 2 ? -(result + 1) / 2 : result / 2;
|
|
27468
|
+
};
|
|
27469
|
+
while (i < str.length) {
|
|
27470
|
+
lat += next();
|
|
27471
|
+
lng += next();
|
|
27472
|
+
out.push([lng / factor, lat / factor]);
|
|
27473
|
+
}
|
|
27474
|
+
return out;
|
|
27475
|
+
}
|
|
27476
|
+
|
|
27477
|
+
/** Encodes [[lng, lat], …] as a polyline. */
|
|
27478
|
+
function encodePolyline(coords, precision) {
|
|
27479
|
+
var factor = Math.pow(10, precision === undefined ? 5 : precision);
|
|
27480
|
+
var out = '', plat = 0, plng = 0;
|
|
27481
|
+
var enc = function (v) {
|
|
27482
|
+
var n = v < 0 ? -2 * v - 1 : 2 * v;
|
|
27483
|
+
while (n >= 32) { out += String.fromCharCode((32 | (n % 32)) + 63); n = Math.floor(n / 32); }
|
|
27484
|
+
out += String.fromCharCode(n + 63);
|
|
27485
|
+
};
|
|
27486
|
+
coords.forEach(function (c) {
|
|
27487
|
+
var lat = Math.round(c[1] * factor), lng = Math.round(c[0] * factor);
|
|
27488
|
+
enc(lat - plat); enc(lng - plng);
|
|
27489
|
+
plat = lat; plng = lng;
|
|
27490
|
+
});
|
|
27491
|
+
return out;
|
|
27492
|
+
}
|
|
27493
|
+
|
|
27494
|
+
function readPolyline(text, precision) {
|
|
27495
|
+
var c = decodePolyline(text, precision);
|
|
27496
|
+
return fc(c.length ? [feature({}, { type: 'LineString', coordinates: c })] : []);
|
|
27497
|
+
}
|
|
27498
|
+
|
|
27499
|
+
// WKT: one or more geometries, separated by white space, commas, semicolons or new lines.
|
|
27500
|
+
function readWkt(text) {
|
|
27501
|
+
var s = String(text), i = 0, features = [];
|
|
27502
|
+
var ws = function () { while (i < s.length && /[\s,;]/.test(s[i])) { i++; } };
|
|
27503
|
+
var spaces = function () { while (i < s.length && /\s/.test(s[i])) { i++; } };
|
|
27504
|
+
var word = function () { spaces(); var m = /^[A-Za-z]+/.exec(s.slice(i)); if (!m) { return ''; } i += m[0].length; return m[0].toUpperCase(); };
|
|
27505
|
+
var expect = function (c) { spaces(); if (s[i] !== c) { throw new Error('sMap: WKT: "' + c + '" expected at ' + i); } i++; };
|
|
27506
|
+
var peek = function () { spaces(); return s[i]; };
|
|
27507
|
+
var dims = { z: false, m: false };
|
|
27508
|
+
var point = function () {
|
|
27509
|
+
var nums = [];
|
|
27510
|
+
for (;;) {
|
|
27511
|
+
spaces();
|
|
27512
|
+
var m = /^[-+]?(\d+\.?\d*|\.\d+)([eE][-+]?\d+)?/.exec(s.slice(i));
|
|
27513
|
+
if (!m) { break; }
|
|
27514
|
+
nums.push(Number(m[0])); i += m[0].length;
|
|
27515
|
+
}
|
|
27516
|
+
if (nums.length < 2) { throw new Error('sMap: WKT: a position needs two numbers at ' + i); }
|
|
27517
|
+
var hasZ = dims.z || (!dims.m && nums.length >= 3);
|
|
27518
|
+
return hasZ && nums.length >= 3 ? [nums[0], nums[1], nums[2]] : [nums[0], nums[1]];
|
|
27519
|
+
};
|
|
27520
|
+
var list = function (item) {
|
|
27521
|
+
expect('(');
|
|
27522
|
+
var out = [item()];
|
|
27523
|
+
while (peek() === ',') { i++; out.push(item()); }
|
|
27524
|
+
expect(')');
|
|
27525
|
+
return out;
|
|
27526
|
+
};
|
|
27527
|
+
var empty = function () { var save = i; if (word() === 'EMPTY') { return true; } i = save; return false; };
|
|
27528
|
+
var geometry = function () {
|
|
27529
|
+
spaces();
|
|
27530
|
+
var m = /^SRID=\d+;/i.exec(s.slice(i));
|
|
27531
|
+
if (m) { i += m[0].length; }
|
|
27532
|
+
var type = word();
|
|
27533
|
+
if (!type) { throw new Error('sMap: WKT: a geometry type expected at ' + i); }
|
|
27534
|
+
var flag = null;
|
|
27535
|
+
// The flag may be glued to the type: POINTZ, LINESTRINGM, POLYGONZM are POINT Z, LINESTRING M, POLYGON ZM.
|
|
27536
|
+
var glued = /^(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)(ZM|Z|M)$/.exec(type);
|
|
27537
|
+
if (glued) { type = glued[1]; flag = glued[2]; } else {
|
|
27538
|
+
var save = i;
|
|
27539
|
+
flag = word();
|
|
27540
|
+
if (flag !== 'Z' && flag !== 'M' && flag !== 'ZM') { i = save; flag = null; }
|
|
27541
|
+
}
|
|
27542
|
+
dims = { z: flag === 'Z' || flag === 'ZM', m: flag === 'M' || flag === 'ZM' };
|
|
27543
|
+
if (empty()) { return null; }
|
|
27544
|
+
var ring = function () { return list(point); };
|
|
27545
|
+
switch (type) {
|
|
27546
|
+
case 'POINT': return { type: 'Point', coordinates: list(point)[0] };
|
|
27547
|
+
case 'LINESTRING': return { type: 'LineString', coordinates: list(point) };
|
|
27548
|
+
case 'POLYGON': return { type: 'Polygon', coordinates: list(ring) };
|
|
27549
|
+
case 'MULTIPOINT': return { type: 'MultiPoint', coordinates: list(function () { if (peek() === '(') { return list(point)[0]; } return point(); }) };
|
|
27550
|
+
case 'MULTILINESTRING': return { type: 'MultiLineString', coordinates: list(ring) };
|
|
27551
|
+
case 'MULTIPOLYGON': return { type: 'MultiPolygon', coordinates: list(function () { return list(ring); }) };
|
|
27552
|
+
case 'GEOMETRYCOLLECTION': {
|
|
27553
|
+
var outer = dims;
|
|
27554
|
+
var g = list(function () { var x = geometry(); dims = outer; return x; }).filter(Boolean);
|
|
27555
|
+
return { type: 'GeometryCollection', geometries: g };
|
|
27556
|
+
}
|
|
27557
|
+
}
|
|
27558
|
+
throw new Error('sMap: WKT: unknown geometry type ' + type);
|
|
27559
|
+
};
|
|
27560
|
+
ws();
|
|
27561
|
+
while (i < s.length) { features.push(feature({}, geometry())); ws(); }
|
|
27562
|
+
return fc(features);
|
|
27563
|
+
}
|
|
27564
|
+
|
|
27565
|
+
function readWkbGeometry(bytes, at) {
|
|
27566
|
+
var dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength), p = at.p;
|
|
27567
|
+
var little = bytes[p] === 1; p += 1;
|
|
27568
|
+
var raw = dv.getUint32(p, little); p += 4;
|
|
27569
|
+
var z = !!(raw & 0x80000000), m = !!(raw & 0x40000000);
|
|
27570
|
+
if (raw & 0x20000000) { p += 4; }
|
|
27571
|
+
var type = raw & 0x0fffffff;
|
|
27572
|
+
if (type >= 1000) { var k = Math.floor(type / 1000); z = z || k === 1 || k === 3; m = m || k === 2 || k === 3; type %= 1000; }
|
|
27573
|
+
var dims = 2 + (z ? 1 : 0) + (m ? 1 : 0);
|
|
27574
|
+
var pos = function () {
|
|
27575
|
+
var c = [dv.getFloat64(p, little), dv.getFloat64(p + 8, little)];
|
|
27576
|
+
if (z) { c.push(dv.getFloat64(p + 16, little)); }
|
|
27577
|
+
p += 8 * dims;
|
|
27578
|
+
return c;
|
|
27579
|
+
};
|
|
27580
|
+
var u32 = function () { var v = dv.getUint32(p, little); p += 4; return v; };
|
|
27581
|
+
var seq = function () { var n = u32(), out = []; for (var i = 0; i < n; i++) { out.push(pos()); } return out; };
|
|
27582
|
+
var parts = function () { var n = u32(), out = []; at.p = p; for (var i = 0; i < n; i++) { out.push(readWkbGeometry(bytes, at)); } p = at.p; return out; };
|
|
27583
|
+
var g;
|
|
27584
|
+
switch (type) {
|
|
27585
|
+
case 1: { var c = pos(); g = c.every(function (v) { return isNaN(v); }) ? null : { type: 'Point', coordinates: c }; break; }
|
|
27586
|
+
case 2: g = { type: 'LineString', coordinates: seq() }; break;
|
|
27587
|
+
case 3: { var n = u32(), rings = []; for (var r = 0; r < n; r++) { rings.push(seq()); } g = { type: 'Polygon', coordinates: rings }; break; }
|
|
27588
|
+
case 4: g = { type: 'MultiPoint', coordinates: parts().filter(Boolean).map(function (x) { return x.coordinates; }) }; break;
|
|
27589
|
+
case 5: g = { type: 'MultiLineString', coordinates: parts().filter(Boolean).map(function (x) { return x.coordinates; }) }; break;
|
|
27590
|
+
case 6: g = { type: 'MultiPolygon', coordinates: parts().filter(Boolean).map(function (x) { return x.coordinates; }) }; break;
|
|
27591
|
+
case 7: g = { type: 'GeometryCollection', geometries: parts().filter(Boolean) }; break;
|
|
27592
|
+
default: throw new Error('sMap: WKB: unknown geometry type ' + type);
|
|
27593
|
+
}
|
|
27594
|
+
if (g && g.type !== 'Point' && g.type !== 'GeometryCollection' && g.coordinates.length === 0) { g = null; }
|
|
27595
|
+
if (g && g.type === 'GeometryCollection' && g.geometries.length === 0) { g = null; }
|
|
27596
|
+
at.p = p;
|
|
27597
|
+
return g;
|
|
27598
|
+
}
|
|
27599
|
+
|
|
27600
|
+
// WKB or EWKB: bytes, or hex text (one geometry per line; a leading \x is allowed).
|
|
27601
|
+
function readWkb(input) {
|
|
27602
|
+
var chunks = [];
|
|
27603
|
+
if (typeof input === 'string') {
|
|
27604
|
+
input.split(/\s+/).forEach(function (h) {
|
|
27605
|
+
h = h.replace(/^\\x/i, '');
|
|
27606
|
+
if (!h) { return; }
|
|
27607
|
+
if (!/^[0-9a-fA-F]+$/.test(h) || h.length % 2) { throw new Error('sMap: WKB text must be hexadecimal'); }
|
|
27608
|
+
var b = new Uint8Array(h.length / 2);
|
|
27609
|
+
for (var i = 0; i < b.length; i++) { b[i] = parseInt(h.substr(i * 2, 2), 16); }
|
|
27610
|
+
chunks.push(b);
|
|
27611
|
+
});
|
|
27612
|
+
} else { chunks.push(toBytes(input)); }
|
|
27613
|
+
var features = [];
|
|
27614
|
+
chunks.forEach(function (b) {
|
|
27615
|
+
var at = { p: 0 };
|
|
27616
|
+
while (at.p < b.length) { features.push(feature({}, readWkbGeometry(b, at))); }
|
|
27617
|
+
});
|
|
27618
|
+
return fc(features);
|
|
27619
|
+
}
|
|
27620
|
+
|
|
27621
|
+
var LAT_HEADINGS = ['lat', 'latitude', 'y', 'lat_dd', 'latitude_dd', 'ycoord', 'y_coord', 'point_y', '纬度'];
|
|
27622
|
+
var LNG_HEADINGS = ['lng', 'lon', 'long', 'longitude', 'x', 'lng_dd', 'lon_dd', 'longitude_dd', 'xcoord', 'x_coord', 'point_x', '经度'];
|
|
27623
|
+
|
|
27624
|
+
function csvRows(text, delim) {
|
|
27625
|
+
var rows = [], row = [], field = '', q = false, i = 0, s = text;
|
|
27626
|
+
for (; i < s.length; i++) {
|
|
27627
|
+
var c = s[i];
|
|
27628
|
+
if (q) {
|
|
27629
|
+
if (c === '"') { if (s[i + 1] === '"') { field += '"'; i++; } else { q = false; } } else { field += c; }
|
|
27630
|
+
} else if (c === '"' && field === '') { q = true; }
|
|
27631
|
+
else if (c === delim) { row.push(field); field = ''; }
|
|
27632
|
+
else if (c === '\n' || c === '\r') {
|
|
27633
|
+
if (c === '\r' && s[i + 1] === '\n') { i++; }
|
|
27634
|
+
row.push(field); field = ''; rows.push(row); row = [];
|
|
27635
|
+
} else { field += c; }
|
|
27636
|
+
}
|
|
27637
|
+
if (field !== '' || row.length) { row.push(field); rows.push(row); }
|
|
27638
|
+
return rows.filter(function (r) { return !(r.length === 1 && r[0].trim() === ''); });
|
|
27639
|
+
}
|
|
27640
|
+
|
|
27641
|
+
function readCsv(text, o) {
|
|
27642
|
+
text = stripBom(String(text));
|
|
27643
|
+
var firstLine = text.split(/\r?\n/)[0] || '';
|
|
27644
|
+
var delim = [',', ';', '\t', '|'].map(function (d) { return [d, firstLine.split(d).length]; }).sort(function (a, b) { return b[1] - a[1]; })[0][0];
|
|
27645
|
+
var rows = csvRows(text, delim);
|
|
27646
|
+
if (!rows.length) { return fc([]); }
|
|
27647
|
+
var head = rows[0].map(function (h) { return h.trim(); });
|
|
27648
|
+
var norm = head.map(function (h) { return h.toLowerCase().replace(/\s+/g, '_'); });
|
|
27649
|
+
var find = function (given, candidates, what) {
|
|
27650
|
+
if (given !== undefined && given !== null) {
|
|
27651
|
+
var at = head.indexOf(String(given));
|
|
27652
|
+
if (at < 0) { at = norm.indexOf(String(given).toLowerCase()); }
|
|
27653
|
+
if (at < 0) { throw new Error('sMap: CSV has no column "' + given + '"'); }
|
|
27654
|
+
return at;
|
|
27655
|
+
}
|
|
27656
|
+
for (var i = 0; i < candidates.length; i++) { var k = norm.indexOf(candidates[i]); if (k >= 0) { return k; } }
|
|
27657
|
+
throw new Error('sMap: CSV: no ' + what + ' column found; name it with the ' + what + ' option');
|
|
27658
|
+
};
|
|
27659
|
+
var la = find(o.lat, LAT_HEADINGS, 'lat'), ln = find(o.lng, LNG_HEADINGS, 'lng');
|
|
27660
|
+
// A coordinate is a whole number text (never a prefix of one); with ';' or tab delimiters a decimal comma counts.
|
|
27661
|
+
var decimalComma = delim === ';' || delim === '\t';
|
|
27662
|
+
var coord = function (v) {
|
|
27663
|
+
v = (v === undefined ? '' : v).trim();
|
|
27664
|
+
if (decimalComma && /^[-+]?\d*,\d+([eE][-+]?\d+)?$/.test(v)) { v = v.replace(',', '.'); }
|
|
27665
|
+
return /^[-+]?(\d+\.?\d*|\.\d+)([eE][-+]?\d+)?$/.test(v) ? Number(v) : NaN;
|
|
27666
|
+
};
|
|
27667
|
+
var features = [];
|
|
27668
|
+
for (var r = 1; r < rows.length; r++) {
|
|
27669
|
+
var row = rows[r].map(function (cell) { return cell.trim(); });
|
|
27670
|
+
var lat = coord(row[la]), lng = coord(row[ln]);
|
|
27671
|
+
if (!isFinite(lat) || !isFinite(lng) || Math.abs(lat) > 90 || Math.abs(lng) > 180) { continue; }
|
|
27672
|
+
var props = {};
|
|
27673
|
+
for (var c = 0; c < head.length; c++) { if (c !== la && c !== ln) { props[head[c]] = row[c] !== undefined ? row[c] : ''; } }
|
|
27674
|
+
features.push(feature(props, { type: 'Point', coordinates: [lng, lat] }));
|
|
27675
|
+
}
|
|
27676
|
+
return fc(features);
|
|
27677
|
+
}
|
|
27678
|
+
|
|
27679
|
+
function readTopojson(topo) {
|
|
27680
|
+
if (!topo || topo.type !== 'Topology') { throw new Error('sMap: not a TopoJSON topology'); }
|
|
27681
|
+
var tr = topo.transform;
|
|
27682
|
+
var kx = tr ? tr.scale[0] : 1, ky = tr ? tr.scale[1] : 1, dx = tr ? tr.translate[0] : 0, dy = tr ? tr.translate[1] : 0;
|
|
27683
|
+
var arcs = (topo.arcs || []).map(function (arc) {
|
|
27684
|
+
var x = 0, y = 0;
|
|
27685
|
+
return arc.map(function (p) {
|
|
27686
|
+
if (tr) { x += p[0]; y += p[1]; var q = [x * kx + dx, y * ky + dy]; return p.length > 2 ? q.concat(p.slice(2)) : q; }
|
|
27687
|
+
return p.slice();
|
|
27688
|
+
});
|
|
27689
|
+
});
|
|
27690
|
+
var arc = function (i) { return i < 0 ? arcs[~i].slice().reverse() : arcs[i]; };
|
|
27691
|
+
var line = function (ids) {
|
|
27692
|
+
var out = [];
|
|
27693
|
+
ids.forEach(function (id) { var a = arc(id); out = out.concat(out.length ? a.slice(1) : a); });
|
|
27694
|
+
return out;
|
|
27695
|
+
};
|
|
27696
|
+
var pt = function (p) { if (!tr) { return p.slice(); } var q = [p[0] * kx + dx, p[1] * ky + dy]; return p.length > 2 ? q.concat(p.slice(2)) : q; };
|
|
27697
|
+
var geom = function (o) {
|
|
27698
|
+
switch (o.type) {
|
|
27699
|
+
case 'Point': return { type: 'Point', coordinates: pt(o.coordinates) };
|
|
27700
|
+
case 'MultiPoint': return { type: 'MultiPoint', coordinates: o.coordinates.map(pt) };
|
|
27701
|
+
case 'LineString': return { type: 'LineString', coordinates: line(o.arcs) };
|
|
27702
|
+
case 'MultiLineString': return { type: 'MultiLineString', coordinates: o.arcs.map(line) };
|
|
27703
|
+
case 'Polygon': return { type: 'Polygon', coordinates: o.arcs.map(line) };
|
|
27704
|
+
case 'MultiPolygon': return { type: 'MultiPolygon', coordinates: o.arcs.map(function (p) { return p.map(line); }) };
|
|
27705
|
+
case 'GeometryCollection': return { type: 'GeometryCollection', geometries: (o.geometries || []).map(geom).filter(Boolean) };
|
|
27706
|
+
}
|
|
27707
|
+
return null;
|
|
27708
|
+
};
|
|
27709
|
+
var features = [];
|
|
27710
|
+
Object.keys(topo.objects || {}).forEach(function (name) {
|
|
27711
|
+
var o = topo.objects[name];
|
|
27712
|
+
var list = o.type === 'GeometryCollection' ? o.geometries || [] : [o];
|
|
27713
|
+
list.forEach(function (g) { features.push(feature(g.properties ? JSON.parse(JSON.stringify(g.properties)) : {}, geom(g), g.id)); });
|
|
27714
|
+
});
|
|
27715
|
+
return fc(features);
|
|
27716
|
+
}
|
|
27717
|
+
|
|
27718
|
+
function readGeojson(data) {
|
|
27719
|
+
if (!data) { return fc([]); }
|
|
27720
|
+
var norm = function (f) { return feature(f.properties || {}, f.geometry || null, f.id); };
|
|
27721
|
+
if (data.type === 'FeatureCollection') { return fc((data.features || []).map(norm)); }
|
|
27722
|
+
if (data.type === 'Feature') { return fc([norm(data)]); }
|
|
27723
|
+
if (GEOMETRY_TYPES.indexOf(data.type) >= 0) { return fc([feature({}, data)]); }
|
|
27724
|
+
throw new Error('sMap: not GeoJSON');
|
|
27725
|
+
}
|
|
27726
|
+
|
|
27727
|
+
var EXTENSIONS = { kml: 'kml', kmz: 'kmz', gpx: 'gpx', wkt: 'wkt', wkb: 'wkb', csv: 'csv', tsv: 'csv', txt: null,
|
|
27728
|
+
topojson: 'topojson', geojson: 'geojson', json: null, polyline: 'polyline', polyline6: 'polyline6' };
|
|
27729
|
+
|
|
27730
|
+
/** The format of a file name's extension, or null. */
|
|
27731
|
+
function formatOfName(name) {
|
|
27732
|
+
var m = /\.([A-Za-z0-9]+)$/.exec(String(name || ''));
|
|
27733
|
+
return m && EXTENSIONS[m[1].toLowerCase()] ? EXTENSIONS[m[1].toLowerCase()] : null;
|
|
27734
|
+
}
|
|
27735
|
+
|
|
27736
|
+
/** Guesses the format from the content: zip → kmz, XML root kml / gpx, JSON Topology / GeoJSON, WKT, hex WKB, CSV. */
|
|
27737
|
+
function sniff(input) {
|
|
27738
|
+
var bytes = typeof input === 'string' || (input && typeof input === 'object' && !toBytes(input)) ? null : toBytes(input);
|
|
27739
|
+
if (bytes && bytes[0] === 0x50 && bytes[1] === 0x4b) { return 'kmz'; }
|
|
27740
|
+
if (bytes && (bytes[0] === 0 || bytes[0] === 1) && bytes.length >= 21) { return 'wkb'; }
|
|
27741
|
+
if (input && typeof input === 'object' && !bytes) { return input.type === 'Topology' ? 'topojson' : 'geojson'; }
|
|
27742
|
+
var s = stripBom(bytes ? utf8(bytes) : String(input)).trim();
|
|
27743
|
+
if (s[0] === '<') {
|
|
27744
|
+
if (/<kml[\s>]/i.test(s)) { return 'kml'; }
|
|
27745
|
+
if (/<gpx[\s>]/i.test(s)) { return 'gpx'; }
|
|
27746
|
+
return null;
|
|
27747
|
+
}
|
|
27748
|
+
if (s[0] === '{') { return /"type"\s*:\s*"Topology"/.test(s) ? 'topojson' : 'geojson'; }
|
|
27749
|
+
if (/^(SRID=\d+;)?\s*(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)\b/i.test(s)) { return 'wkt'; }
|
|
27750
|
+
if (/^(\\x)?0[01][0-9a-fA-F]{8}/.test(s) && /^[\s0-9a-fA-Fx\\]+$/.test(s)) { return 'wkb'; }
|
|
27751
|
+
var first = s.split(/\r?\n/)[0].toLowerCase();
|
|
27752
|
+
if (/[,;\t|]/.test(first) && LAT_HEADINGS.concat(LNG_HEADINGS).some(function (h) { return first.split(/[,;\t|]/).map(function (x) { return x.trim().replace(/^"|"$/g, '').replace(/\s+/g, '_'); }).indexOf(h) >= 0; })) { return 'csv'; }
|
|
27753
|
+
return null;
|
|
27754
|
+
}
|
|
27755
|
+
|
|
27756
|
+
/**
|
|
27757
|
+
* Reads a file into a GeoJSON FeatureCollection, on the device (M29/M30).
|
|
27758
|
+
* @param input text, bytes (ArrayBuffer / Uint8Array), or a parsed JSON object (GeoJSON, TopoJSON)
|
|
27759
|
+
* @param o {format, lat, lng, name}: format one of FORMATS (guessed from `name`'s extension or the content when
|
|
27760
|
+
* omitted; polylines are never guessed); lat / lng name the CSV columns (guessed from common headings)
|
|
27761
|
+
*/
|
|
27762
|
+
function read(input, o) {
|
|
27763
|
+
o = o || {};
|
|
27764
|
+
var format = o.format ? String(o.format).toLowerCase() : (formatOfName(o.name) || sniff(input));
|
|
27765
|
+
if (!format) { throw new Error('sMap: the file format could not be recognised; pass {format}'); }
|
|
27766
|
+
if (FORMATS.indexOf(format) < 0) { throw new Error('sMap: unknown format "' + format + '"'); }
|
|
27767
|
+
var text = function () { if (typeof input === 'string') { return input; } var b = toBytes(input); if (!b) { throw new Error('sMap: ' + format + ' needs text or bytes'); } return utf8(b); };
|
|
27768
|
+
var json = function () { return input && typeof input === 'object' && !toBytes(input) ? input : JSON.parse(stripBom(text())); };
|
|
27769
|
+
switch (format) {
|
|
27770
|
+
case 'kml': return readKml(text());
|
|
27771
|
+
case 'kmz': { var b = toBytes(input); if (!b) { throw new Error('sMap: kmz needs bytes'); } return readKmz(b); }
|
|
27772
|
+
case 'gpx': return readGpx(text());
|
|
27773
|
+
case 'polyline': return readPolyline(text(), 5);
|
|
27774
|
+
case 'polyline6': return readPolyline(text(), 6);
|
|
27775
|
+
case 'wkt': return readWkt(text());
|
|
27776
|
+
case 'wkb': return readWkb(typeof input === 'string' ? input : (toBytes(input) && /^[\s0-9a-fA-Fx\\]+$/.test(utf8(toBytes(input)).trim()) && toBytes(input)[0] !== 0 && toBytes(input)[0] !== 1 ? utf8(toBytes(input)).trim() : input));
|
|
27777
|
+
case 'csv': return readCsv(text(), o);
|
|
27778
|
+
case 'topojson': return readTopojson(json());
|
|
27779
|
+
case 'geojson': return readGeojson(json());
|
|
27780
|
+
}
|
|
27781
|
+
return fc([]);
|
|
27782
|
+
}
|
|
27783
|
+
|
|
27784
|
+
/** A Blob or File (or a fetch Response) read as `read` would: the format comes from the file name when not given. */
|
|
27785
|
+
function readFile(file, o) {
|
|
27786
|
+
o = Object.assign({}, o || {});
|
|
27787
|
+
if (!o.name && file && file.name) { o.name = file.name; }
|
|
27788
|
+
if (!o.name && file && file.url) { o.name = String(file.url).split('?')[0]; }
|
|
27789
|
+
return Promise.resolve(file.arrayBuffer()).then(function (buf) { return read(new Uint8Array(buf), o); });
|
|
27790
|
+
}
|
|
27791
|
+
|
|
27792
|
+
// ------------------------------------------------------------------ formats: write
|
|
27793
|
+
|
|
27794
|
+
function xmlEscape(v) { return String(v).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); }
|
|
27795
|
+
|
|
27796
|
+
// '#rgb' / '#rrggbb' + opacity → KML aabbggrr; anything else null.
|
|
27797
|
+
function toKmlColor(color, opacity) {
|
|
27798
|
+
var m = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.exec(String(color || '').trim());
|
|
27799
|
+
if (!m) { return null; }
|
|
27800
|
+
var h = m[1].length === 3 ? m[1].replace(/./g, '$&$&') : m[1];
|
|
27801
|
+
var a = Math.round(Math.max(0, Math.min(1, opacity === undefined || opacity === null || opacity === '' ? 1 : Number(opacity))) * 255);
|
|
27802
|
+
var aa = (a < 16 ? '0' : '') + a.toString(16);
|
|
27803
|
+
return (aa + h.slice(4, 6) + h.slice(2, 4) + h.slice(0, 2)).toLowerCase();
|
|
27804
|
+
}
|
|
27805
|
+
|
|
27806
|
+
var SIMPLESTYLE = ['marker-color', 'marker-size', 'marker-symbol', 'stroke', 'stroke-width', 'stroke-opacity', 'fill', 'fill-opacity', 'title', 'description'];
|
|
27807
|
+
|
|
27808
|
+
function kmlCoordText(c) { return c.map(function (p) { return p.slice(0, 3).join(','); }).join(' '); }
|
|
27809
|
+
function kmlGeom(g) {
|
|
27810
|
+
if (!g) { return ''; }
|
|
27811
|
+
switch (g.type) {
|
|
27812
|
+
case 'Point': return '<Point><coordinates>' + kmlCoordText([g.coordinates]) + '</coordinates></Point>';
|
|
27813
|
+
case 'LineString': return '<LineString><coordinates>' + kmlCoordText(g.coordinates) + '</coordinates></LineString>';
|
|
27814
|
+
case 'Polygon': return '<Polygon>' + g.coordinates.map(function (r, i) {
|
|
27815
|
+
var tag = i === 0 ? 'outerBoundaryIs' : 'innerBoundaryIs';
|
|
27816
|
+
return '<' + tag + '><LinearRing><coordinates>' + kmlCoordText(r) + '</coordinates></LinearRing></' + tag + '>';
|
|
27817
|
+
}).join('') + '</Polygon>';
|
|
27818
|
+
case 'MultiPoint': return '<MultiGeometry>' + g.coordinates.map(function (c) { return kmlGeom({ type: 'Point', coordinates: c }); }).join('') + '</MultiGeometry>';
|
|
27819
|
+
case 'MultiLineString': return '<MultiGeometry>' + g.coordinates.map(function (c) { return kmlGeom({ type: 'LineString', coordinates: c }); }).join('') + '</MultiGeometry>';
|
|
27820
|
+
case 'MultiPolygon': return '<MultiGeometry>' + g.coordinates.map(function (c) { return kmlGeom({ type: 'Polygon', coordinates: c }); }).join('') + '</MultiGeometry>';
|
|
27821
|
+
case 'GeometryCollection': return '<MultiGeometry>' + g.geometries.map(kmlGeom).join('') + '</MultiGeometry>';
|
|
27822
|
+
}
|
|
27823
|
+
return '';
|
|
27824
|
+
}
|
|
27825
|
+
|
|
27826
|
+
/**
|
|
27827
|
+
* A FeatureCollection as a KML 2.2 document: one Placemark per feature (name from `name` or `title`, `description`,
|
|
27828
|
+
* simplestyle properties as an inline Style, every other property as ExtendedData).
|
|
27829
|
+
*/
|
|
27830
|
+
function writeKml(data, o) {
|
|
27831
|
+
o = o || {};
|
|
27832
|
+
var features = readGeojson(data).features;
|
|
27833
|
+
var out = '<?xml version="1.0" encoding="UTF-8"?>\n<kml xmlns="http://www.opengis.net/kml/2.2"><Document>' +
|
|
27834
|
+
(o.name ? '<name>' + xmlEscape(o.name) + '</name>' : '') + '\n';
|
|
27835
|
+
features.forEach(function (f) {
|
|
27836
|
+
var p = f.properties || {};
|
|
27837
|
+
var name = p.name !== undefined && p.name !== null ? p.name : p.title;
|
|
27838
|
+
var s = '<Placemark>';
|
|
27839
|
+
if (name !== undefined && name !== null) { s += '<name>' + xmlEscape(name) + '</name>'; }
|
|
27840
|
+
if (p.description !== undefined && p.description !== null) { s += '<description>' + xmlEscape(p.description) + '</description>'; }
|
|
27841
|
+
var style = '';
|
|
27842
|
+
var icon = toKmlColor(p['marker-color']);
|
|
27843
|
+
if (icon) { style += '<IconStyle><color>' + icon + '</color></IconStyle>'; }
|
|
27844
|
+
var line = toKmlColor(p.stroke || (p['stroke-opacity'] !== undefined || p['stroke-width'] !== undefined ? '#555555' : null), p['stroke-opacity']);
|
|
27845
|
+
if (line || p['stroke-width'] !== undefined) {
|
|
27846
|
+
style += '<LineStyle>' + (line ? '<color>' + line + '</color>' : '') + (p['stroke-width'] !== undefined ? '<width>' + Number(p['stroke-width']) + '</width>' : '') + '</LineStyle>';
|
|
27847
|
+
}
|
|
27848
|
+
var poly = toKmlColor(p.fill || (p['fill-opacity'] !== undefined ? '#555555' : null), p['fill-opacity']);
|
|
27849
|
+
if (poly) { style += '<PolyStyle><color>' + poly + '</color></PolyStyle>'; }
|
|
27850
|
+
if (style) { s += '<Style>' + style + '</Style>'; }
|
|
27851
|
+
var extra = Object.keys(p).filter(function (k) { return k !== 'name' && SIMPLESTYLE.indexOf(k) < 0 && p[k] !== null && p[k] !== undefined; });
|
|
27852
|
+
if (p.name !== undefined && p.name !== null && p.title !== undefined && p.title !== null) { extra.push('title'); }
|
|
27853
|
+
if (extra.length) {
|
|
27854
|
+
s += '<ExtendedData>' + extra.map(function (k) {
|
|
27855
|
+
var v = p[k];
|
|
27856
|
+
return '<Data name="' + xmlEscape(k) + '"><value>' + xmlEscape(typeof v === 'object' ? JSON.stringify(v) : v) + '</value></Data>';
|
|
27857
|
+
}).join('') + '</ExtendedData>';
|
|
27858
|
+
}
|
|
27859
|
+
out += s + kmlGeom(f.geometry) + '</Placemark>\n';
|
|
27860
|
+
});
|
|
27861
|
+
return out + '</Document></kml>\n';
|
|
27862
|
+
}
|
|
27863
|
+
|
|
27864
|
+
/** Writes a FeatureCollection as 'geojson' (text) or 'kml'. */
|
|
27865
|
+
function write(data, format, o) {
|
|
27866
|
+
format = String(format || 'geojson').toLowerCase();
|
|
27867
|
+
if (format === 'geojson') { return JSON.stringify(readGeojson(data)); }
|
|
27868
|
+
if (format === 'kml') { return writeKml(data, o); }
|
|
27869
|
+
throw new Error('sMap: cannot write format "' + format + '" (geojson, kml)');
|
|
27870
|
+
}
|
|
27871
|
+
|
|
27872
|
+
// ------------------------------------------------------------------ the converting endpoint (contract §6)
|
|
27873
|
+
|
|
27874
|
+
var CONVERT_LIMIT = 20 * 1024 * 1024;
|
|
27875
|
+
|
|
27876
|
+
/**
|
|
27877
|
+
* Converts a file on sMap (`POST /v1/convert`, contract §6): for large files or formats a caller cannot parse.
|
|
27878
|
+
* @param input a Blob / File, bytes or text
|
|
27879
|
+
* @param o {format (guessed from the file name or content when omitted), lat, lng, baseUrl, apiKey, token}
|
|
27880
|
+
* @returns Promise of a GeoJSON FeatureCollection
|
|
27881
|
+
*/
|
|
27882
|
+
function convert(input, o) {
|
|
27883
|
+
o = o || {};
|
|
27884
|
+
if (typeof fetch === 'undefined') { return Promise.reject(new Error('sMap: convert needs fetch')); }
|
|
27885
|
+
var base = String(o.baseUrl || DEFAULT_BASE).replace(/\/$/, '');
|
|
27886
|
+
var bodyOf = input && typeof input.arrayBuffer === 'function' && !(input instanceof Uint8Array)
|
|
27887
|
+
? Promise.resolve(input.arrayBuffer()).then(function (b) { return new Uint8Array(b); })
|
|
27888
|
+
: Promise.resolve(typeof input === 'string' ? input : toBytes(input));
|
|
27889
|
+
return bodyOf.then(function (body) {
|
|
27890
|
+
var size = typeof body === 'string' ? body.length : body.length;
|
|
27891
|
+
if (size > CONVERT_LIMIT) { throw new Error('sMap: convert takes files of at most 20 MB'); }
|
|
27892
|
+
var format = o.format ? String(o.format).toLowerCase() : (formatOfName(o.name || (input && input.name)) || sniff(body));
|
|
27893
|
+
if (!format || FORMATS.indexOf(format) < 0) { throw new Error('sMap: the file format could not be recognised; pass {format}'); }
|
|
27894
|
+
var url = base + '/v1/convert?from=' + encodeURIComponent(format);
|
|
27895
|
+
if (o.lat) { url += '&lat=' + encodeURIComponent(o.lat); }
|
|
27896
|
+
if (o.lng) { url += '&lng=' + encodeURIComponent(o.lng); }
|
|
27897
|
+
var headers = { 'Content-Type': 'application/octet-stream' };
|
|
27898
|
+
if (o.token) { headers.Authorization = 'Bearer ' + o.token; }
|
|
27899
|
+
if (o.apiKey) { headers['X-SMap-Key'] = o.apiKey; }
|
|
27900
|
+
return fetch(url, { method: 'POST', headers: headers, body: body }).then(function (res) {
|
|
27901
|
+
if (!res.ok) {
|
|
27902
|
+
return res.text().then(function (t) {
|
|
27903
|
+
var msg = t;
|
|
27904
|
+
try { var j = JSON.parse(t); msg = j.message || j.error || t; } catch (e) { /* plain text */ }
|
|
27905
|
+
throw new Error('sMap: convert failed (HTTP ' + res.status + ')' + (msg ? ': ' + msg : ''));
|
|
27906
|
+
});
|
|
27907
|
+
}
|
|
27908
|
+
return res.json();
|
|
27909
|
+
});
|
|
27910
|
+
});
|
|
27911
|
+
}
|
|
27912
|
+
|
|
27913
|
+
// ------------------------------------------------------------------ URLs
|
|
27914
|
+
|
|
27915
|
+
function styleName(style) {
|
|
27916
|
+
style = String(style || 'light');
|
|
27917
|
+
if (FLAVORS.indexOf(style) >= 0 || /^custom:[A-Za-z0-9_-]+$/.test(style)) { return style; }
|
|
27918
|
+
throw new Error('sMap: unknown style "' + style + '" (a scheme or custom:<id>)');
|
|
27919
|
+
}
|
|
27920
|
+
|
|
27921
|
+
function query(o) {
|
|
27922
|
+
var q = [];
|
|
27923
|
+
if (o.lang) { q.push('lang=' + encodeURIComponent(o.lang)); }
|
|
27924
|
+
if (o.apiKey) { q.push('key=' + encodeURIComponent(o.apiKey)); }
|
|
27925
|
+
return q.length ? '?' + q.join('&') : '';
|
|
27926
|
+
}
|
|
27927
|
+
|
|
27928
|
+
/** The style URL of a scheme or a custom style (`custom:<id>` → /v1/styles/custom/<id>). */
|
|
27929
|
+
function styleUrl(style, o) {
|
|
27930
|
+
o = o || {};
|
|
27931
|
+
var base = String(o.baseUrl || DEFAULT_BASE).replace(/\/$/, '');
|
|
27932
|
+
var s = styleName(style);
|
|
27933
|
+
var path = s.indexOf('custom:') === 0 ? '/v1/styles/custom/' + encodeURIComponent(s.slice(7)) : '/v1/styles/' + s;
|
|
27934
|
+
return base + path + query({ lang: o.lang || o.language || 'en', apiKey: o.apiKey });
|
|
27935
|
+
}
|
|
27936
|
+
|
|
27937
|
+
/**
|
|
27938
|
+
* sMap's raster tiles (contract §5) as an XYZ template, for tools without vector tiles:
|
|
27939
|
+
* `${baseUrl}/v1/raster/<style>/{z}/{x}/{y}[@2x].png?lang=…[&key=…]`.
|
|
27940
|
+
* @param o {lang, retina (512 px tiles), baseUrl, apiKey}
|
|
27941
|
+
*/
|
|
27942
|
+
function rasterUrl(style, o) {
|
|
27943
|
+
o = o || {};
|
|
27944
|
+
var base = String(o.baseUrl || DEFAULT_BASE).replace(/\/$/, '');
|
|
27945
|
+
return base + '/v1/raster/' + styleName(style) + '/{z}/{x}/{y}' + (o.retina ? '@2x' : '') + '.png' + query({ lang: o.lang || o.language, apiKey: o.apiKey });
|
|
27946
|
+
}
|
|
27947
|
+
|
|
27948
|
+
/** The TileJSON of sMap's raster tiles of a style. */
|
|
27949
|
+
function rasterTileJsonUrl(style, o) {
|
|
27950
|
+
o = o || {};
|
|
27951
|
+
var base = String(o.baseUrl || DEFAULT_BASE).replace(/\/$/, '');
|
|
27952
|
+
return base + '/v1/raster/' + styleName(style) + '.json' + query({ lang: o.lang || o.language, apiKey: o.apiKey });
|
|
27953
|
+
}
|
|
27954
|
+
|
|
27955
|
+
// Google and Bing tiles are never offered: their licences forbid it (M31).
|
|
27956
|
+
var FORBIDDEN_HOSTS = /(^|\.)(google\.[a-z.]+|googleapis\.com|gstatic\.com|ggpht\.com|virtualearth\.net|bing\.com|bing\.net)$/i;
|
|
27957
|
+
function refuseForbidden(urls) {
|
|
27958
|
+
(urls || []).forEach(function (u) {
|
|
27959
|
+
var m = /^[a-z][a-z0-9+.-]*:\/\/([^/?#:]+)/i.exec(String(u).replace(/^pmtiles:\/\//i, ''));
|
|
27960
|
+
if (m && FORBIDDEN_HOSTS.test(m[1])) { throw new Error('sMap: Google and Bing tiles cannot be used (their licences do not allow it)'); }
|
|
27961
|
+
});
|
|
27962
|
+
}
|
|
27963
|
+
|
|
27964
|
+
/**
|
|
27965
|
+
* A WMS GetMap tile URL template (Web Mercator, the engine fills {bbox-epsg-3857}).
|
|
27966
|
+
* @param o {url, layers, styles, format, transparent, version ('1.3.0' | '1.1.1'), tileSize, params}
|
|
27967
|
+
*/
|
|
27968
|
+
function wmsTileUrl(o) {
|
|
27969
|
+
if (!o || !o.url || !o.layers) { throw new Error('sMap: a WMS layer needs url and layers'); }
|
|
27970
|
+
var version = o.version || '1.3.0', size = o.tileSize || 256;
|
|
27971
|
+
var p = {
|
|
27972
|
+
SERVICE: 'WMS', REQUEST: 'GetMap', VERSION: version,
|
|
27973
|
+
LAYERS: Array.isArray(o.layers) ? o.layers.join(',') : o.layers,
|
|
27974
|
+
STYLES: Array.isArray(o.styles) ? o.styles.join(',') : (o.styles || ''),
|
|
27975
|
+
FORMAT: o.format || 'image/png', TRANSPARENT: o.transparent === false ? 'FALSE' : 'TRUE',
|
|
27976
|
+
WIDTH: String(size), HEIGHT: String(size),
|
|
27977
|
+
};
|
|
27978
|
+
p[version === '1.3.0' ? 'CRS' : 'SRS'] = 'EPSG:3857';
|
|
27979
|
+
Object.keys(o.params || {}).forEach(function (k) { p[k] = String(o.params[k]); });
|
|
27980
|
+
var url = String(o.url);
|
|
27981
|
+
var sep = url.indexOf('?') < 0 ? '?' : (/[?&]$/.test(url) ? '' : '&');
|
|
27982
|
+
return url + sep + Object.keys(p).map(function (k) { return k + '=' + encodeURIComponent(p[k]); }).join('&') + '&BBOX={bbox-epsg-3857}';
|
|
27983
|
+
}
|
|
27984
|
+
|
|
27985
|
+
// ------------------------------------------------------------------ WMTS capabilities
|
|
27986
|
+
|
|
27987
|
+
var WEB_MERCATOR_SCALE0 = 559082264.0287178; // scale denominator of zoom 0 (256 px tiles, 0.28 mm pixels)
|
|
27988
|
+
var MERCATOR_CRS = /(3857|900913|3785|102100|102113|GoogleMapsCompatible)/i;
|
|
27989
|
+
|
|
27990
|
+
/** Reads WMTS 1.0.0 capabilities: {layers: [{id, title, styles, formats, tileMatrixSets, resourceUrls, bounds}], tileMatrixSets: {id: {crs, matrices: [{id, zoom}]}}, getTile: kvp href}. */
|
|
27991
|
+
function parseWmtsCapabilities(xml) {
|
|
27992
|
+
var doc = parseXml(stripBom(String(xml)));
|
|
27993
|
+
var caps = descendants(doc, 'Capabilities')[0] || doc;
|
|
27994
|
+
var contents = elem(caps, 'Contents');
|
|
27995
|
+
if (!contents) { throw new Error('sMap: not WMTS capabilities'); }
|
|
27996
|
+
var sets = {};
|
|
27997
|
+
elems(contents, 'TileMatrixSet').forEach(function (s) {
|
|
27998
|
+
var id = trimmed(elem(s, 'Identifier'));
|
|
27999
|
+
var crs = trimmed(elem(s, 'SupportedCRS'));
|
|
28000
|
+
var matrices = elems(s, 'TileMatrix').map(function (m) {
|
|
28001
|
+
var scale = parseFloat(trimmed(elem(m, 'ScaleDenominator')));
|
|
28002
|
+
return { id: trimmed(elem(m, 'Identifier')), zoom: Math.round(Math.log(WEB_MERCATOR_SCALE0 / scale) / Math.LN2), tileWidth: parseInt(trimmed(elem(m, 'TileWidth')), 10) || 256 };
|
|
28003
|
+
});
|
|
28004
|
+
sets[id] = { id: id, crs: crs, wellKnown: trimmed(elem(s, 'WellKnownScaleSet')), matrices: matrices };
|
|
28005
|
+
});
|
|
28006
|
+
var layers = elems(contents, 'Layer').map(function (l) {
|
|
28007
|
+
var bbox = elem(l, 'WGS84BoundingBox');
|
|
28008
|
+
var lower = bbox ? trimmed(elem(bbox, 'LowerCorner')).split(/\s+/).map(Number) : null;
|
|
28009
|
+
var upper = bbox ? trimmed(elem(bbox, 'UpperCorner')).split(/\s+/).map(Number) : null;
|
|
28010
|
+
return {
|
|
28011
|
+
id: trimmed(elem(l, 'Identifier')),
|
|
28012
|
+
title: trimmed(elem(l, 'Title')),
|
|
28013
|
+
styles: elems(l, 'Style').map(function (s) { return { id: trimmed(elem(s, 'Identifier')), isDefault: s.attrs.isDefault === 'true' }; }),
|
|
28014
|
+
formats: elems(l, 'Format').map(trimmed),
|
|
28015
|
+
tileMatrixSets: elems(l, 'TileMatrixSetLink').map(function (k) { return trimmed(elem(k, 'TileMatrixSet')); }),
|
|
28016
|
+
resourceUrls: elems(l, 'ResourceURL').filter(function (r) { return (r.attrs.resourceType || 'tile') === 'tile'; }).map(function (r) { return { format: r.attrs.format, template: r.attrs.template }; }),
|
|
28017
|
+
bounds: lower && upper && lower.length >= 2 && upper.length >= 2 ? [lower[0], lower[1], upper[0], upper[1]] : null,
|
|
28018
|
+
};
|
|
28019
|
+
});
|
|
28020
|
+
var getTile = null;
|
|
28021
|
+
descendants(caps, 'Operation').forEach(function (op) {
|
|
28022
|
+
if (op.attrs.name !== 'GetTile') { return; }
|
|
28023
|
+
descendants(op, 'Get').forEach(function (g) {
|
|
28024
|
+
var enc = descendants(g, 'Value').map(trimmed);
|
|
28025
|
+
if (!getTile && (!enc.length || enc.indexOf('KVP') >= 0)) { getTile = attr(g, 'href'); }
|
|
28026
|
+
});
|
|
28027
|
+
});
|
|
28028
|
+
return { layers: layers, tileMatrixSets: sets, getTile: getTile };
|
|
28029
|
+
}
|
|
28030
|
+
|
|
28031
|
+
// The zoom → matrix identifier rule of a set, as a template piece for {z}: identifiers equal to the zoom, or one
|
|
28032
|
+
// prefix followed by the zoom (EPSG:3857:0, …). Anything else cannot be a {z} template.
|
|
28033
|
+
function matrixTemplate(set) {
|
|
28034
|
+
var ms = set.matrices.slice().sort(function (a, b) { return a.zoom - b.zoom; });
|
|
28035
|
+
if (!ms.length) { throw new Error('sMap: WMTS matrix set ' + set.id + ' has no matrices'); }
|
|
28036
|
+
var prefix = null;
|
|
28037
|
+
for (var i = 0; i < ms.length; i++) {
|
|
28038
|
+
var m = /^(.*?)(\d+)$/.exec(ms[i].id);
|
|
28039
|
+
if (!m || Number(m[2]) !== ms[i].zoom) { throw new Error('sMap: WMTS matrix set ' + set.id + ': matrix ' + ms[i].id + ' is not named by its zoom'); }
|
|
28040
|
+
if (prefix === null) { prefix = m[1]; } else if (prefix !== m[1]) { throw new Error('sMap: WMTS matrix set ' + set.id + ' mixes matrix names'); }
|
|
28041
|
+
}
|
|
28042
|
+
return { prefix: prefix, minzoom: ms[0].zoom, maxzoom: ms[ms.length - 1].zoom, tileSize: ms[0].tileWidth };
|
|
28043
|
+
}
|
|
28044
|
+
|
|
28045
|
+
/**
|
|
28046
|
+
* A raster source for one WMTS layer from its capabilities (KVP or REST; Web Mercator matrix sets only).
|
|
28047
|
+
* @param caps the text of GetCapabilities or parseWmtsCapabilities' answer
|
|
28048
|
+
* @param o {layer, tileMatrixSet, style, format, encoding ('rest' | 'kvp'), url (where the capabilities came
|
|
28049
|
+
* from, for a relative GetTile address)}
|
|
28050
|
+
*/
|
|
28051
|
+
function wmtsSource(caps, o) {
|
|
28052
|
+
o = o || {};
|
|
28053
|
+
if (typeof caps === 'string') { caps = parseWmtsCapabilities(caps); }
|
|
28054
|
+
var layer = o.layer ? caps.layers.filter(function (l) { return l.id === o.layer; })[0] : caps.layers[0];
|
|
28055
|
+
if (!layer) { throw new Error('sMap: WMTS has no layer ' + (o.layer || '')); }
|
|
28056
|
+
var setId = o.tileMatrixSet || layer.tileMatrixSets.filter(function (id) {
|
|
28057
|
+
var s = caps.tileMatrixSets[id];
|
|
28058
|
+
return s && (MERCATOR_CRS.test(s.crs) || MERCATOR_CRS.test(s.wellKnown) || MERCATOR_CRS.test(id));
|
|
28059
|
+
})[0];
|
|
28060
|
+
var set = caps.tileMatrixSets[setId];
|
|
28061
|
+
if (!set) { throw new Error('sMap: WMTS layer ' + layer.id + ' has no Web Mercator matrix set'); }
|
|
28062
|
+
var mt = matrixTemplate(set);
|
|
28063
|
+
var style = o.style || (layer.styles.filter(function (s) { return s.isDefault; })[0] || layer.styles[0] || { id: 'default' }).id;
|
|
28064
|
+
var format = o.format || layer.formats.filter(function (f) { return /png/i.test(f); })[0] || layer.formats[0] || 'image/png';
|
|
28065
|
+
var rest = layer.resourceUrls.filter(function (r) { return !o.format || r.format === o.format; })[0] || layer.resourceUrls[0];
|
|
28066
|
+
var tile;
|
|
28067
|
+
if (rest && o.encoding !== 'kvp') {
|
|
28068
|
+
tile = rest.template.replace(/\{TileMatrixSet\}/gi, set.id).replace(/\{TileMatrix\}/gi, mt.prefix + '{z}')
|
|
28069
|
+
.replace(/\{TileRow\}/gi, '{y}').replace(/\{TileCol\}/gi, '{x}').replace(/\{Style\}/gi, style);
|
|
28070
|
+
} else {
|
|
28071
|
+
var href = caps.getTile || o.url;
|
|
28072
|
+
if (!href) { throw new Error('sMap: WMTS capabilities name no GetTile address'); }
|
|
28073
|
+
href = String(href);
|
|
28074
|
+
var sep = href.indexOf('?') < 0 ? '?' : (/[?&]$/.test(href) ? '' : '&');
|
|
28075
|
+
if (sep === '?' && o.url && href === o.url) { href = href.split('?')[0]; }
|
|
28076
|
+
tile = href + sep + 'SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=' + encodeURIComponent(layer.id) + '&STYLE=' + encodeURIComponent(style) +
|
|
28077
|
+
'&FORMAT=' + encodeURIComponent(format) + '&TILEMATRIXSET=' + encodeURIComponent(set.id) + '&TILEMATRIX=' + encodeURIComponent(mt.prefix) + '{z}&TILEROW={y}&TILECOL={x}';
|
|
28078
|
+
}
|
|
28079
|
+
var src = { type: 'raster', tiles: [tile], tileSize: mt.tileSize, minzoom: mt.minzoom, maxzoom: mt.maxzoom };
|
|
28080
|
+
if (layer.bounds) { src.bounds = layer.bounds; }
|
|
28081
|
+
return src;
|
|
28082
|
+
}
|
|
28083
|
+
|
|
28084
|
+
// ------------------------------------------------------------------ PMTiles v3 (archives read by HTTP range)
|
|
28085
|
+
|
|
28086
|
+
function zxyToTileId(z, x, y) {
|
|
28087
|
+
if (z > 26) { throw new Error('sMap: zoom above 26'); }
|
|
28088
|
+
var acc = (Math.pow(4, z) - 1) / 3;
|
|
28089
|
+
var n = Math.pow(2, z), rx, ry, d = 0, xy = [x, y];
|
|
28090
|
+
for (var s = n / 2; s >= 1; s /= 2) {
|
|
28091
|
+
rx = (xy[0] & s) > 0 ? 1 : 0;
|
|
28092
|
+
ry = (xy[1] & s) > 0 ? 1 : 0;
|
|
28093
|
+
d += s * s * ((3 * rx) ^ ry);
|
|
28094
|
+
if (ry === 0) { if (rx === 1) { xy[0] = s - 1 - xy[0]; xy[1] = s - 1 - xy[1]; } var t = xy[0]; xy[0] = xy[1]; xy[1] = t; }
|
|
28095
|
+
}
|
|
28096
|
+
return acc + d;
|
|
28097
|
+
}
|
|
28098
|
+
|
|
28099
|
+
function readVarint(b, at) {
|
|
28100
|
+
var v = 0, mul = 1, byte;
|
|
28101
|
+
do { byte = b[at.p++]; v += (byte & 0x7f) * mul; mul *= 128; } while (byte & 0x80);
|
|
28102
|
+
return v;
|
|
28103
|
+
}
|
|
28104
|
+
|
|
28105
|
+
function pmDirectory(bytes) {
|
|
28106
|
+
var at = { p: 0 }, n = readVarint(bytes, at), entries = new Array(n), last = 0, i;
|
|
28107
|
+
for (i = 0; i < n; i++) { last += readVarint(bytes, at); entries[i] = { tileId: last, offset: 0, length: 0, runLength: 1 }; }
|
|
28108
|
+
for (i = 0; i < n; i++) { entries[i].runLength = readVarint(bytes, at); }
|
|
28109
|
+
for (i = 0; i < n; i++) { entries[i].length = readVarint(bytes, at); }
|
|
28110
|
+
for (i = 0; i < n; i++) {
|
|
28111
|
+
var v = readVarint(bytes, at);
|
|
28112
|
+
entries[i].offset = v === 0 && i > 0 ? entries[i - 1].offset + entries[i - 1].length : v - 1;
|
|
28113
|
+
}
|
|
28114
|
+
return entries;
|
|
28115
|
+
}
|
|
28116
|
+
|
|
28117
|
+
function pmFind(entries, tileId) {
|
|
28118
|
+
var lo = 0, hi = entries.length - 1;
|
|
28119
|
+
while (lo <= hi) {
|
|
28120
|
+
var mid = (lo + hi) >> 1, c = entries[mid].tileId - tileId;
|
|
28121
|
+
if (c < 0) { lo = mid + 1; } else if (c > 0) { hi = mid - 1; } else { return entries[mid]; }
|
|
28122
|
+
}
|
|
28123
|
+
if (hi >= 0) {
|
|
28124
|
+
var e = entries[hi];
|
|
28125
|
+
if (e.runLength === 0) { return e; }
|
|
28126
|
+
if (tileId - e.tileId < e.runLength) { return e; }
|
|
28127
|
+
}
|
|
28128
|
+
return null;
|
|
28129
|
+
}
|
|
28130
|
+
|
|
28131
|
+
function u64(dv, p) { return dv.getUint32(p, true) + dv.getUint32(p + 4, true) * 4294967296; }
|
|
28132
|
+
|
|
28133
|
+
/** Reads the PMTiles v3 header (the first 127 bytes). */
|
|
28134
|
+
function pmHeader(bytes) {
|
|
28135
|
+
var dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
28136
|
+
if (utf8(bytes.subarray(0, 7)) !== 'PMTiles') { throw new Error('sMap: not a PMTiles archive'); }
|
|
28137
|
+
if (bytes[7] !== 3) { throw new Error('sMap: PMTiles version ' + bytes[7] + ' is not supported (3)'); }
|
|
28138
|
+
return {
|
|
28139
|
+
rootOffset: u64(dv, 8), rootLength: u64(dv, 16), metadataOffset: u64(dv, 24), metadataLength: u64(dv, 32),
|
|
28140
|
+
leafOffset: u64(dv, 40), leafLength: u64(dv, 48), dataOffset: u64(dv, 56), dataLength: u64(dv, 64),
|
|
28141
|
+
internalCompression: bytes[97], tileCompression: bytes[98], tileType: bytes[99], minZoom: bytes[100], maxZoom: bytes[101],
|
|
28142
|
+
bounds: [dv.getInt32(102, true) / 1e7, dv.getInt32(106, true) / 1e7, dv.getInt32(110, true) / 1e7, dv.getInt32(114, true) / 1e7],
|
|
28143
|
+
centerZoom: bytes[118], center: [dv.getInt32(119, true) / 1e7, dv.getInt32(123, true) / 1e7],
|
|
28144
|
+
};
|
|
28145
|
+
}
|
|
28146
|
+
|
|
28147
|
+
function pmDecompress(bytes, compression) {
|
|
28148
|
+
if (compression === 0 || compression === 1) { return bytes; }
|
|
28149
|
+
if (compression === 2) { return gunzip(bytes); }
|
|
28150
|
+
throw new Error('sMap: PMTiles compression ' + compression + ' is not supported');
|
|
28151
|
+
}
|
|
28152
|
+
|
|
28153
|
+
/**
|
|
28154
|
+
* A PMTiles v3 archive read by HTTP range requests. `fetchRange(url, offset, length)` → Promise of bytes (default:
|
|
28155
|
+
* fetch with a Range header).
|
|
28156
|
+
*/
|
|
28157
|
+
function PMTilesArchive(url, fetchRange) {
|
|
28158
|
+
this.url = url;
|
|
28159
|
+
this._fetch = fetchRange || function (u, offset, length) {
|
|
28160
|
+
return fetch(u, { headers: { Range: 'bytes=' + offset + '-' + (offset + length - 1) } }).then(function (res) {
|
|
28161
|
+
if (!res.ok) { throw new Error('sMap: PMTiles HTTP ' + res.status); }
|
|
28162
|
+
return res.arrayBuffer();
|
|
28163
|
+
}).then(function (b) { return new Uint8Array(b); });
|
|
28164
|
+
};
|
|
28165
|
+
this._header = null;
|
|
28166
|
+
this._dirs = {};
|
|
28167
|
+
}
|
|
28168
|
+
PMTilesArchive.prototype.header = function () {
|
|
28169
|
+
var self = this;
|
|
28170
|
+
if (!this._header) {
|
|
28171
|
+
this._header = Promise.resolve(this._fetch(this.url, 0, 16384)).then(function (first) {
|
|
28172
|
+
first = toBytes(first);
|
|
28173
|
+
var h = pmHeader(first);
|
|
28174
|
+
if (h.rootOffset + h.rootLength <= first.length) { self._dirs['root'] = pmDirectory(pmDecompress(first.subarray(h.rootOffset, h.rootOffset + h.rootLength), h.internalCompression)); }
|
|
28175
|
+
return h;
|
|
28176
|
+
});
|
|
28177
|
+
}
|
|
28178
|
+
return this._header;
|
|
28179
|
+
};
|
|
28180
|
+
PMTilesArchive.prototype._dir = function (key, offset, length, compression) {
|
|
28181
|
+
var self = this;
|
|
28182
|
+
if (this._dirs[key]) { return Promise.resolve(this._dirs[key]); }
|
|
28183
|
+
return Promise.resolve(this._fetch(this.url, offset, length)).then(function (b) {
|
|
28184
|
+
var d = pmDirectory(pmDecompress(toBytes(b), compression));
|
|
28185
|
+
self._dirs[key] = d;
|
|
28186
|
+
return d;
|
|
28187
|
+
});
|
|
28188
|
+
};
|
|
28189
|
+
PMTilesArchive.prototype.metadata = function () {
|
|
28190
|
+
var self = this;
|
|
28191
|
+
return this.header().then(function (h) {
|
|
28192
|
+
if (!h.metadataLength) { return {}; }
|
|
28193
|
+
return Promise.resolve(self._fetch(self.url, h.metadataOffset, h.metadataLength)).then(function (b) {
|
|
28194
|
+
try { return JSON.parse(utf8(pmDecompress(toBytes(b), h.internalCompression))); } catch (e) { return {}; }
|
|
28195
|
+
});
|
|
28196
|
+
});
|
|
28197
|
+
};
|
|
28198
|
+
/** The tile's bytes (decompressed), or null when the archive has none. */
|
|
28199
|
+
PMTilesArchive.prototype.tile = function (z, x, y) {
|
|
28200
|
+
var self = this, id = zxyToTileId(z, x, y);
|
|
28201
|
+
return this.header().then(function (h) {
|
|
28202
|
+
if (z < h.minZoom || z > h.maxZoom) { return null; }
|
|
28203
|
+
var walk = function (key, offset, length, depth) {
|
|
28204
|
+
return self._dir(key, offset, length, h.internalCompression).then(function (entries) {
|
|
28205
|
+
var e = pmFind(entries, id);
|
|
28206
|
+
if (!e) { return null; }
|
|
28207
|
+
if (e.runLength > 0) {
|
|
28208
|
+
return Promise.resolve(self._fetch(self.url, h.dataOffset + e.offset, e.length)).then(function (b) { return pmDecompress(toBytes(b), h.tileCompression); });
|
|
28209
|
+
}
|
|
28210
|
+
if (depth > 3) { return null; }
|
|
28211
|
+
return walk('leaf:' + e.offset, h.leafOffset + e.offset, e.length, depth + 1);
|
|
28212
|
+
});
|
|
28213
|
+
};
|
|
28214
|
+
return walk('root', h.rootOffset, h.rootLength, 0);
|
|
28215
|
+
});
|
|
28216
|
+
};
|
|
28217
|
+
/** TileJSON for the engine: tiles through the pmtiles:// protocol. */
|
|
28218
|
+
PMTilesArchive.prototype.tileJSON = function () {
|
|
28219
|
+
var self = this;
|
|
28220
|
+
return Promise.all([this.header(), this.metadata()]).then(function (r) {
|
|
28221
|
+
var h = r[0], meta = r[1] || {};
|
|
28222
|
+
var tj = { tilejson: '3.0.0', tiles: ['pmtiles://' + self.url + '/{z}/{x}/{y}'], minzoom: h.minZoom, maxzoom: h.maxZoom, bounds: h.bounds };
|
|
28223
|
+
if (meta.vector_layers) { tj.vector_layers = meta.vector_layers; }
|
|
28224
|
+
if (meta.attribution) { tj.attribution = meta.attribution; }
|
|
28225
|
+
tj.tileType = h.tileType;
|
|
28226
|
+
return tj;
|
|
28227
|
+
});
|
|
28228
|
+
};
|
|
28229
|
+
|
|
28230
|
+
var archives = {};
|
|
28231
|
+
/**
|
|
28232
|
+
* The `pmtiles://` protocol for the engine (engine.addProtocol('pmtiles', pmtilesProtocol)): `pmtiles://<url>`
|
|
28233
|
+
* answers TileJSON, `pmtiles://<url>/{z}/{x}/{y}` the tile.
|
|
28234
|
+
*/
|
|
28235
|
+
function pmtilesProtocol(params) {
|
|
28236
|
+
var rest = String(params.url).replace(/^pmtiles:\/\//, '');
|
|
28237
|
+
var m = /^(.*)\/(\d+)\/(\d+)\/(\d+)$/.exec(rest);
|
|
28238
|
+
var url = m ? m[1] : rest;
|
|
28239
|
+
var a = archives[url] || (archives[url] = new PMTilesArchive(url));
|
|
28240
|
+
if (!m) { return a.tileJSON().then(function (tj) { return { data: tj }; }); }
|
|
28241
|
+
return a.tile(Number(m[2]), Number(m[3]), Number(m[4])).then(function (b) {
|
|
28242
|
+
return { data: b ? b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength) : new ArrayBuffer(0) };
|
|
28243
|
+
});
|
|
28244
|
+
}
|
|
28245
|
+
|
|
28246
|
+
// ------------------------------------------------------------------ deck.gl (optional, M36)
|
|
28247
|
+
|
|
28248
|
+
var deckModule = null;
|
|
28249
|
+
/** Registers deck.gl's map overlay: useDeck(await import('@deck.gl/mapbox')) or useDeck({ MapboxOverlay }). */
|
|
28250
|
+
function useDeck(mod) { deckModule = mod && (mod.MapboxOverlay ? mod : (mod['default'] && mod['default'].MapboxOverlay ? mod['default'] : null)); if (!deckModule) { throw new Error('sMap: useDeck needs the @deck.gl/mapbox module (MapboxOverlay)'); } return deckModule; }
|
|
28251
|
+
function deck() {
|
|
28252
|
+
if (deckModule) { return deckModule; }
|
|
28253
|
+
var g = typeof globalThis !== 'undefined' ? globalThis : (typeof window !== 'undefined' ? window : {});
|
|
28254
|
+
if (g.deck && g.deck.MapboxOverlay) { return g.deck; }
|
|
28255
|
+
return null;
|
|
28256
|
+
}
|
|
28257
|
+
|
|
28258
|
+
// ------------------------------------------------------------------ layer description (M32, shared/LAYER-JSON.md)
|
|
28259
|
+
|
|
28260
|
+
function jsonSafe(v) {
|
|
28261
|
+
if (v === null || typeof v === 'number' || typeof v === 'string' || typeof v === 'boolean') { return v; }
|
|
28262
|
+
if (typeof v === 'function' || v === undefined) { return undefined; }
|
|
28263
|
+
if (Array.isArray(v)) { return v.map(function (x) { var y = jsonSafe(x); return y === undefined ? null : y; }); }
|
|
28264
|
+
if (typeof v === 'object') {
|
|
28265
|
+
var o = {};
|
|
28266
|
+
Object.keys(v).forEach(function (k) { var y = jsonSafe(v[k]); if (y !== undefined) { o[k] = y; } });
|
|
28267
|
+
return o;
|
|
28268
|
+
}
|
|
28269
|
+
return undefined;
|
|
28270
|
+
}
|
|
28271
|
+
|
|
28272
|
+
/** Checks a layer description's outer shape (LAYER-JSON.md); throws with a message. */
|
|
28273
|
+
function checkLayerJson(json) {
|
|
28274
|
+
if (typeof json === 'string') { json = JSON.parse(json); }
|
|
28275
|
+
if (!json || json.type !== 'smap.layers') { throw new Error('sMap: not an sMap layer description (type smap.layers)'); }
|
|
28276
|
+
if (typeof json.version !== 'number' || Math.floor(json.version) > 1) { throw new Error('sMap: layer description version ' + json.version + ' is newer than this SDK reads (1)'); }
|
|
28277
|
+
if (!Array.isArray(json.layers)) { throw new Error('sMap: a layer description needs layers'); }
|
|
28278
|
+
json.layers.forEach(function (l, i) {
|
|
28279
|
+
if (!l || typeof l.id !== 'string' || !l.id) { throw new Error('sMap: layer ' + i + ' has no id'); }
|
|
28280
|
+
if (['points', 'lines', 'polygons', 'style'].indexOf(l.kind) < 0) { throw new Error('sMap: layer ' + l.id + ' has an unknown kind ' + l.kind); }
|
|
28281
|
+
if (l.slot !== undefined && SLOTS.indexOf(l.slot) < 0) { throw new Error('sMap: layer ' + l.id + ' has an unknown slot ' + l.slot); }
|
|
28282
|
+
});
|
|
28283
|
+
return json;
|
|
28284
|
+
}
|
|
28285
|
+
|
|
28286
|
+
return {
|
|
28287
|
+
DEFAULT_BASE: DEFAULT_BASE,
|
|
28288
|
+
SLOTS: SLOTS,
|
|
28289
|
+
basemapClasses: basemapClasses,
|
|
28290
|
+
basemapLayerIndex: basemapLayerIndex,
|
|
28291
|
+
applyBasemapOverrides: applyBasemapOverrides,
|
|
28292
|
+
overrideOrder: overrideOrder,
|
|
28293
|
+
effectiveOverrides: effectiveOverrides,
|
|
28294
|
+
scaledWidth: scaledWidth,
|
|
28295
|
+
slotAnchors: slotAnchors,
|
|
28296
|
+
formats: {
|
|
28297
|
+
list: FORMATS.slice(),
|
|
28298
|
+
read: read,
|
|
28299
|
+
readFile: readFile,
|
|
28300
|
+
write: write,
|
|
28301
|
+
toKML: writeKml,
|
|
28302
|
+
sniff: sniff,
|
|
28303
|
+
formatOfName: formatOfName,
|
|
28304
|
+
decodePolyline: decodePolyline,
|
|
28305
|
+
encodePolyline: encodePolyline,
|
|
28306
|
+
},
|
|
28307
|
+
convert: convert,
|
|
28308
|
+
styleUrl: styleUrl,
|
|
28309
|
+
rasterUrl: rasterUrl,
|
|
28310
|
+
rasterTileJsonUrl: rasterTileJsonUrl,
|
|
28311
|
+
wmsTileUrl: wmsTileUrl,
|
|
28312
|
+
refuseForbidden: refuseForbidden,
|
|
28313
|
+
parseWmtsCapabilities: parseWmtsCapabilities,
|
|
28314
|
+
wmtsSource: wmtsSource,
|
|
28315
|
+
PMTilesArchive: PMTilesArchive,
|
|
28316
|
+
pmtilesProtocol: pmtilesProtocol,
|
|
28317
|
+
zxyToTileId: zxyToTileId,
|
|
28318
|
+
inflateRaw: inflateRaw,
|
|
28319
|
+
gunzip: gunzip,
|
|
28320
|
+
unzip: unzip,
|
|
28321
|
+
parseXml: parseXml,
|
|
28322
|
+
useDeck: useDeck,
|
|
28323
|
+
deck: deck,
|
|
28324
|
+
jsonSafe: jsonSafe,
|
|
28325
|
+
checkLayerJson: checkLayerJson,
|
|
28326
|
+
};
|
|
28327
|
+
});
|
|
28328
|
+
|
|
28329
|
+
return module.exports;
|
|
28330
|
+
}
|
|
28331
|
+
function smapCreateSdk(engine, interop) {
|
|
26853
28332
|
var module = { exports: {} };
|
|
26854
28333
|
var exports = module.exports;
|
|
26855
28334
|
var define;
|
|
26856
28335
|
var require = function (name) { throw new Error('sMap: unexpected require of ' + name); };
|
|
26857
|
-
var self = { maplibregl: engine };
|
|
28336
|
+
var self = { maplibregl: engine, smapInterop: interop };
|
|
26858
28337
|
/*!
|
|
26859
28338
|
* sMap Web SDK — maps from Superroute's sMap service.
|
|
26860
28339
|
*
|
|
@@ -26866,14 +28345,15 @@ function smapCreateSdk(engine) {
|
|
|
26866
28345
|
*/
|
|
26867
28346
|
(function (root, factory) {
|
|
26868
28347
|
if (typeof module === 'object' && module.exports) {
|
|
26869
|
-
module.exports = factory(root.maplibregl !== undefined ? root.maplibregl : require('maplibre-gl')
|
|
28348
|
+
module.exports = factory(root.maplibregl !== undefined ? root.maplibregl : require('maplibre-gl'),
|
|
28349
|
+
root.smapInterop !== undefined ? root.smapInterop : require('./smap-interop.js'));
|
|
26870
28350
|
} else {
|
|
26871
|
-
root.sMap = factory(root.maplibregl);
|
|
28351
|
+
root.sMap = factory(root.maplibregl, root.smapInterop);
|
|
26872
28352
|
}
|
|
26873
|
-
})(typeof self !== 'undefined' ? self : this, function (engine) {
|
|
28353
|
+
})(typeof self !== 'undefined' ? self : this, function (engine, interop) {
|
|
26874
28354
|
'use strict';
|
|
26875
28355
|
|
|
26876
|
-
var VERSION = '1.
|
|
28356
|
+
var VERSION = '1.1.1';
|
|
26877
28357
|
var FLAVORS = ['light', 'white', 'grayscale', 'dark', 'black'];
|
|
26878
28358
|
var BASEMAP_SOURCE = 'basemap';
|
|
26879
28359
|
var BUILDINGS_LAYER = 'smap-buildings-3d';
|
|
@@ -26881,6 +28361,8 @@ function smapCreateSdk(engine) {
|
|
|
26881
28361
|
// The public sMap address, unless the page names another (`baseUrl`). Never derived from where this file was loaded:
|
|
26882
28362
|
// the SDK is published apart from the map service.
|
|
26883
28363
|
var DEFAULT_BASE = 'https://tile.srsr.ca';
|
|
28364
|
+
var CUSTOM_PREFIX = 'custom:';
|
|
28365
|
+
var SLOTS = ['bottom', 'roads', 'labels', 'top'];
|
|
26884
28366
|
|
|
26885
28367
|
function tokenExpiry(token) {
|
|
26886
28368
|
try {
|
|
@@ -27194,6 +28676,9 @@ function smapCreateSdk(engine) {
|
|
|
27194
28676
|
},
|
|
27195
28677
|
};
|
|
27196
28678
|
|
|
28679
|
+
// A number from a feature property, `fallback` when it has none.
|
|
28680
|
+
function numberProp(name, fallback) { return ['to-number', ['coalesce', ['get', name], fallback], fallback]; }
|
|
28681
|
+
|
|
27197
28682
|
function drawnImageId(kind, parts) { return ['concat', IMAGE_PREFIX + kind].concat(parts.reduce(function (a, p) { return a.concat(['|', p]); }, [])); }
|
|
27198
28683
|
|
|
27199
28684
|
var NODE_TTL = 3600 * 1000;
|
|
@@ -27289,12 +28774,26 @@ function smapCreateSdk(engine) {
|
|
|
27289
28774
|
this._token = options.token || null;
|
|
27290
28775
|
this._getToken = options.getToken || null;
|
|
27291
28776
|
this._apiKey = options.apiKey || null;
|
|
27292
|
-
this._flavor =
|
|
27293
|
-
this._customStyle =
|
|
28777
|
+
this._flavor = 'light';
|
|
28778
|
+
this._customStyle = null;
|
|
28779
|
+
this._serverStyle = null;
|
|
28780
|
+
this._takeStyle(options.style);
|
|
27294
28781
|
this._language = options.language || (typeof navigator !== 'undefined' && navigator.language) || 'en';
|
|
27295
28782
|
this._projection = options.projection || 'mercator';
|
|
27296
28783
|
this._layers = {};
|
|
27297
28784
|
this._order = [];
|
|
28785
|
+
// Style Spec sources added with addSource (or by the external-layer helpers), re-added after every style change.
|
|
28786
|
+
this._sources = {};
|
|
28787
|
+
this._sourceOrder = [];
|
|
28788
|
+
// Basemap class overrides (setBasemap), the basemap's own layer ids and their values before any override.
|
|
28789
|
+
this._basemapOverrides = {};
|
|
28790
|
+
this._basemapIds = [];
|
|
28791
|
+
this._basemapOriginal = {};
|
|
28792
|
+
// setPaint / setLayout / setFilter on basemap layers, applied again after a style change.
|
|
28793
|
+
this._basemapEdits = {};
|
|
28794
|
+
this._deckLayers = [];
|
|
28795
|
+
this._deckOverlay = null;
|
|
28796
|
+
this._seq = 0;
|
|
27298
28797
|
this._buildings = false;
|
|
27299
28798
|
this._handlers = {};
|
|
27300
28799
|
this._refreshTimer = null;
|
|
@@ -27338,6 +28837,7 @@ function smapCreateSdk(engine) {
|
|
|
27338
28837
|
this.ready = start.then(function () {
|
|
27339
28838
|
engineOptions.style = self._styleUrl();
|
|
27340
28839
|
self.engine = new engine.Map(engineOptions);
|
|
28840
|
+
deckCompat(self.engine);
|
|
27341
28841
|
if (self.engine.addControl) {
|
|
27342
28842
|
var badge = badgeControl();
|
|
27343
28843
|
self.engine.addControl(badge, 'bottom-right');
|
|
@@ -27357,8 +28857,16 @@ function smapCreateSdk(engine) {
|
|
|
27357
28857
|
if (this._token && this._getToken) { this._scheduleRefresh(); }
|
|
27358
28858
|
}
|
|
27359
28859
|
|
|
28860
|
+
// A scheme, `custom:<id>` (a saved custom style served by sMap), or a style URL / object of your own.
|
|
28861
|
+
SMap.prototype._takeStyle = function (style) {
|
|
28862
|
+
if (typeof style === 'string' && FLAVORS.indexOf(style) >= 0) { this._flavor = style; this._customStyle = null; this._serverStyle = null; }
|
|
28863
|
+
else if (typeof style === 'string' && style.indexOf(CUSTOM_PREFIX) === 0) { this._customStyle = null; this._serverStyle = style; }
|
|
28864
|
+
else if (style) { this._customStyle = style; this._serverStyle = null; }
|
|
28865
|
+
};
|
|
28866
|
+
|
|
27360
28867
|
SMap.prototype._styleUrl = function () {
|
|
27361
28868
|
if (this._customStyle) { return this._customStyle; }
|
|
28869
|
+
if (this._serverStyle) { return interop.styleUrl(this._serverStyle, { baseUrl: this._base, lang: this._language, apiKey: this._apiKey }); }
|
|
27362
28870
|
var url = this._base + '/v1/styles/' + this._flavor + '?lang=' + encodeURIComponent(this._language);
|
|
27363
28871
|
return this._apiKey ? url + '&key=' + encodeURIComponent(this._apiKey) : url;
|
|
27364
28872
|
};
|
|
@@ -27384,7 +28892,7 @@ function smapCreateSdk(engine) {
|
|
|
27384
28892
|
|
|
27385
28893
|
/** Switches the colour scheme ('light', 'white', 'grayscale', 'dark', 'black'); your layers are kept. */
|
|
27386
28894
|
SMap.prototype.setStyle = function (style) {
|
|
27387
|
-
|
|
28895
|
+
this._takeStyle(style);
|
|
27388
28896
|
this._styleReady = false;
|
|
27389
28897
|
this.engine.setStyle(this._styleUrl());
|
|
27390
28898
|
this._styleClass();
|
|
@@ -27396,7 +28904,7 @@ function smapCreateSdk(engine) {
|
|
|
27396
28904
|
var c = this.engine && this.engine.getContainer && this.engine.getContainer();
|
|
27397
28905
|
if (!c || !c.classList) { return; }
|
|
27398
28906
|
for (var i = 0; i < FLAVORS.length; i++) { c.classList.remove('smap-style-' + FLAVORS[i]); }
|
|
27399
|
-
if (!this._customStyle) { c.classList.add('smap-style-' + this._flavor); }
|
|
28907
|
+
if (!this._customStyle && !this._serverStyle) { c.classList.add('smap-style-' + this._flavor); }
|
|
27400
28908
|
};
|
|
27401
28909
|
|
|
27402
28910
|
// Draws an image the SDK named (IMAGE_PREFIX + kind|args) and adds it to the map; other names are left alone.
|
|
@@ -27596,7 +29104,7 @@ function smapCreateSdk(engine) {
|
|
|
27596
29104
|
// Points open at their own position; lines and areas where they were clicked.
|
|
27597
29105
|
var at = hit.kind === 'points' && f.geometry && f.geometry.type === 'Point' ? f.geometry.coordinates : lngLat;
|
|
27598
29106
|
var content;
|
|
27599
|
-
try { content = hit.o.popup(f.properties || {}, f); } catch (error) { this._reportError(error); return; }
|
|
29107
|
+
try { content = (hit.popupFn || hit.o.popup)(f.properties || {}, f); } catch (error) { this._reportError(error); return; }
|
|
27600
29108
|
var popupOptions = Object.assign({}, hit.o.popupOptions || {});
|
|
27601
29109
|
if (hit.o.marker && popupOptions.offset === undefined && this._popupDefaults.offset === undefined) {
|
|
27602
29110
|
// Just above the pin as it is drawn now (its scale may follow the zoom).
|
|
@@ -27646,6 +29154,7 @@ function smapCreateSdk(engine) {
|
|
|
27646
29154
|
|
|
27647
29155
|
SMap.prototype.setLanguage = function (language) {
|
|
27648
29156
|
this._language = language;
|
|
29157
|
+
// A scheme or a custom style of sMap is served per language; a style of your own keeps its labels.
|
|
27649
29158
|
if (!this._customStyle) { this._styleReady = false; this.engine.setStyle(this._styleUrl()); }
|
|
27650
29159
|
return this;
|
|
27651
29160
|
};
|
|
@@ -27699,7 +29208,15 @@ function smapCreateSdk(engine) {
|
|
|
27699
29208
|
if (!this._buildings || !m.getSource(BASEMAP_SOURCE)) { return; }
|
|
27700
29209
|
var o = this._buildingOptions || {};
|
|
27701
29210
|
var dark = this._flavor === 'dark' || this._flavor === 'black';
|
|
29211
|
+
// Under the lowest of your layers, wherever its slot put it.
|
|
27702
29212
|
var firstUserLayer = this._order.length ? this._firstEngineLayer(this._order[0]) : undefined;
|
|
29213
|
+
var st = m.getStyle && m.getStyle();
|
|
29214
|
+
if (st && Array.isArray(st.layers)) {
|
|
29215
|
+
var mine = {}, self = this;
|
|
29216
|
+
this._order.forEach(function (id) { self._layers[id].engineLayers.forEach(function (l) { mine[l] = true; }); });
|
|
29217
|
+
firstUserLayer = undefined;
|
|
29218
|
+
for (var i = 0; i < st.layers.length; i++) { if (mine[st.layers[i].id]) { firstUserLayer = st.layers[i].id; break; } }
|
|
29219
|
+
}
|
|
27703
29220
|
m.addLayer({
|
|
27704
29221
|
id: BUILDINGS_LAYER,
|
|
27705
29222
|
type: 'fill-extrusion',
|
|
@@ -27768,12 +29285,24 @@ function smapCreateSdk(engine) {
|
|
|
27768
29285
|
return this;
|
|
27769
29286
|
};
|
|
27770
29287
|
|
|
29288
|
+
/**
|
|
29289
|
+
* Removes a layer: one of the SDK's own (addPoints, addLines, addPolygons; addData's whole group), a style layer
|
|
29290
|
+
* (addStyleLayer and the external-layer helpers, with the source a helper made for it), or a source of addSource.
|
|
29291
|
+
*/
|
|
27771
29292
|
SMap.prototype.removeLayer = function (id) {
|
|
27772
|
-
var
|
|
27773
|
-
|
|
27774
|
-
|
|
27775
|
-
|
|
27776
|
-
|
|
29293
|
+
var self = this;
|
|
29294
|
+
var ids = this._order.filter(function (x) { return x === id || self._layers[x].group === id; });
|
|
29295
|
+
if (!ids.length) {
|
|
29296
|
+
if (this._sources[id]) { this.removeSource(id); }
|
|
29297
|
+
return this;
|
|
29298
|
+
}
|
|
29299
|
+
ids.forEach(function (x) {
|
|
29300
|
+
var def = self._layers[x];
|
|
29301
|
+
self._drop(x, def);
|
|
29302
|
+
delete self._layers[x];
|
|
29303
|
+
self._order = self._order.filter(function (y) { return y !== x; });
|
|
29304
|
+
if (def.ownsSource && self._sources[def.ownsSource] && !self._sourceInUse(def.ownsSource)) { self.removeSource(def.ownsSource); }
|
|
29305
|
+
});
|
|
27777
29306
|
return this;
|
|
27778
29307
|
};
|
|
27779
29308
|
|
|
@@ -27797,8 +29326,571 @@ function smapCreateSdk(engine) {
|
|
|
27797
29326
|
SMap.prototype.flyTo = function (o) { this.engine.flyTo(o); return this; };
|
|
27798
29327
|
SMap.prototype.remove = function () { clearTimeout(this._refreshTimer); if (this.engine) { this.engine.remove(); } };
|
|
27799
29328
|
|
|
29329
|
+
// ------------------------------------------------------------------ Style Specification sources and layers (M24)
|
|
29330
|
+
|
|
29331
|
+
function clone(v) { return v === undefined ? undefined : JSON.parse(JSON.stringify(v)); }
|
|
29332
|
+
|
|
29333
|
+
function sourceUrls(source) {
|
|
29334
|
+
var urls = [];
|
|
29335
|
+
if (source && source.url) { urls.push(source.url); }
|
|
29336
|
+
if (source && Array.isArray(source.tiles)) { urls = urls.concat(source.tiles); }
|
|
29337
|
+
return urls;
|
|
29338
|
+
}
|
|
29339
|
+
|
|
29340
|
+
// The pmtiles:// protocol, registered on the engine the first time a source needs it.
|
|
29341
|
+
var pmtilesRegistered = false;
|
|
29342
|
+
function needsPmtiles(source) { return sourceUrls(source).some(function (u) { return /^pmtiles:\/\//i.test(String(u)); }); }
|
|
29343
|
+
function registerPmtiles() {
|
|
29344
|
+
if (pmtilesRegistered || !engine || !engine.addProtocol) { return; }
|
|
29345
|
+
engine.addProtocol('pmtiles', interop.pmtilesProtocol);
|
|
29346
|
+
pmtilesRegistered = true;
|
|
29347
|
+
}
|
|
29348
|
+
|
|
29349
|
+
SMap.prototype._engineSource = function (id) {
|
|
29350
|
+
var source = this._sources[id];
|
|
29351
|
+
if (needsPmtiles(source)) { registerPmtiles(); }
|
|
29352
|
+
this.engine.addSource(id, source);
|
|
29353
|
+
};
|
|
29354
|
+
|
|
29355
|
+
SMap.prototype._sourceInUse = function (id) {
|
|
29356
|
+
var self = this;
|
|
29357
|
+
return this._order.some(function (x) { var d = self._layers[x]; return d.kind === 'style' && d.layer.source === id; });
|
|
29358
|
+
};
|
|
29359
|
+
|
|
29360
|
+
/**
|
|
29361
|
+
* Adds a source of the Style Specification v8 (geojson, vector, raster, raster-dem, image, video), passed to the
|
|
29362
|
+
* engine as it is and kept across style and language changes. Its `attribution` is shown beside the map's credit.
|
|
29363
|
+
* `pmtiles://<url>` reads a PMTiles archive. Google and Bing tiles are refused.
|
|
29364
|
+
*/
|
|
29365
|
+
SMap.prototype.addSource = function (id, source) {
|
|
29366
|
+
if (!id || !source || typeof source !== 'object' || !source.type) { throw new Error('sMap: addSource needs an id and a source with a type'); }
|
|
29367
|
+
if (id === BASEMAP_SOURCE) { throw new Error('sMap: the source id "basemap" belongs to the map'); }
|
|
29368
|
+
interop.refuseForbidden(sourceUrls(source));
|
|
29369
|
+
if (this._sources[id]) { this.removeSource(id); }
|
|
29370
|
+
this._sources[id] = source;
|
|
29371
|
+
this._sourceOrder.push(id);
|
|
29372
|
+
if (this.engine && this._styleReady) { this._engineSource(id); }
|
|
29373
|
+
return this;
|
|
29374
|
+
};
|
|
29375
|
+
|
|
29376
|
+
/** Removes a source of addSource and the style layers that read it. */
|
|
29377
|
+
SMap.prototype.removeSource = function (id) {
|
|
29378
|
+
var self = this;
|
|
29379
|
+
if (!this._sources[id]) { return this; }
|
|
29380
|
+
this._order.filter(function (x) { var d = self._layers[x]; return d.kind === 'style' && d.layer.source === id; })
|
|
29381
|
+
.forEach(function (x) { var d = self._layers[x]; d.ownsSource = null; self.removeLayer(x); });
|
|
29382
|
+
delete this._sources[id];
|
|
29383
|
+
this._sourceOrder = this._sourceOrder.filter(function (x) { return x !== id; });
|
|
29384
|
+
var m = this.engine;
|
|
29385
|
+
if (m && m.getSource && m.getSource(id) && m.removeSource) { m.removeSource(id); }
|
|
29386
|
+
return this;
|
|
29387
|
+
};
|
|
29388
|
+
|
|
29389
|
+
/** The sources added with addSource: {id: source}. */
|
|
29390
|
+
SMap.prototype.sources = function () {
|
|
29391
|
+
var out = {}, self = this;
|
|
29392
|
+
this._sourceOrder.forEach(function (id) { out[id] = clone(self._sources[id]); });
|
|
29393
|
+
return out;
|
|
29394
|
+
};
|
|
29395
|
+
|
|
29396
|
+
/**
|
|
29397
|
+
* Adds a layer of the Style Specification v8, passed to the engine as it is: a layer copied from a style editor or
|
|
29398
|
+
* another map page works unchanged. Its `source` is a source of addSource, an inline source, or `basemap`.
|
|
29399
|
+
* @param {Object} layer
|
|
29400
|
+
* @param {Object} [o] {slot: 'bottom' | 'roads' | 'labels' | 'top', before: layer id, zIndex}; without either the
|
|
29401
|
+
* layer draws on top, in the order added
|
|
29402
|
+
*/
|
|
29403
|
+
SMap.prototype.addStyleLayer = function (layer, o) {
|
|
29404
|
+
if (!layer || !layer.id || !layer.type) { throw new Error('sMap: addStyleLayer needs a layer with an id and a type'); }
|
|
29405
|
+
o = o || {};
|
|
29406
|
+
if (typeof layer.source === 'object' && layer.source) { interop.refuseForbidden(sourceUrls(layer.source)); }
|
|
29407
|
+
var def = { kind: 'style', layer: clone(layer), o: { slot: o.slot, before: o.before, zIndex: o.zIndex } };
|
|
29408
|
+
if (layer.layout && layer.layout.visibility === 'none') { def.hidden = true; }
|
|
29409
|
+
this._define(layer.id, def);
|
|
29410
|
+
return this;
|
|
29411
|
+
};
|
|
29412
|
+
|
|
29413
|
+
// The style layer of an id, the basemap's (and any engine layer's) when it is not one of yours.
|
|
29414
|
+
SMap.prototype._edit = function (layerId, apply) {
|
|
29415
|
+
var def = this._layers[layerId];
|
|
29416
|
+
if (def && def.kind !== 'style') { throw new Error('sMap: ' + layerId + ' is a layer of addPoints / addLines / addPolygons; set its look with its options'); }
|
|
29417
|
+
if (def) { apply(def.layer); } else {
|
|
29418
|
+
var e = this._basemapEdits[layerId] = this._basemapEdits[layerId] || { paint: {}, layout: {} };
|
|
29419
|
+
apply(e);
|
|
29420
|
+
}
|
|
29421
|
+
};
|
|
29422
|
+
|
|
29423
|
+
function setKey(obj, group, name, value) {
|
|
29424
|
+
obj[group] = obj[group] || {};
|
|
29425
|
+
if (value === undefined || value === null) { delete obj[group][name]; } else { obj[group][name] = value; }
|
|
29426
|
+
}
|
|
29427
|
+
|
|
29428
|
+
/** Sets a paint property of a style layer (yours or the basemap's); kept across style changes. */
|
|
29429
|
+
SMap.prototype.setPaint = function (layerId, name, value) {
|
|
29430
|
+
this._edit(layerId, function (l) { setKey(l, 'paint', name, value); });
|
|
29431
|
+
var m = this.engine;
|
|
29432
|
+
if (m && this._styleReady && m.getLayer(layerId)) { m.setPaintProperty(layerId, name, value === null ? undefined : value); }
|
|
29433
|
+
return this;
|
|
29434
|
+
};
|
|
29435
|
+
|
|
29436
|
+
/** Sets a layout property of a style layer (yours or the basemap's); kept across style changes. */
|
|
29437
|
+
SMap.prototype.setLayout = function (layerId, name, value) {
|
|
29438
|
+
this._edit(layerId, function (l) { setKey(l, 'layout', name, value); });
|
|
29439
|
+
var def = this._layers[layerId];
|
|
29440
|
+
if (def && name === 'visibility') { def.hidden = value === 'none'; }
|
|
29441
|
+
var m = this.engine;
|
|
29442
|
+
if (m && this._styleReady && m.getLayer(layerId)) { m.setLayoutProperty(layerId, name, value === null ? undefined : value); }
|
|
29443
|
+
return this;
|
|
29444
|
+
};
|
|
29445
|
+
|
|
29446
|
+
/** Sets (or with null clears) the filter of a style layer; kept across style changes. */
|
|
29447
|
+
SMap.prototype.setFilter = function (layerId, filter) {
|
|
29448
|
+
this._edit(layerId, function (l) { if (filter === undefined || filter === null) { delete l.filter; } else { l.filter = filter; } });
|
|
29449
|
+
var m = this.engine;
|
|
29450
|
+
if (m && this._styleReady && m.getLayer(layerId)) { m.setFilter(layerId, filter === undefined ? null : filter); }
|
|
29451
|
+
return this;
|
|
29452
|
+
};
|
|
29453
|
+
|
|
29454
|
+
SMap.prototype._applyEdits = function (id, e) {
|
|
29455
|
+
var m = this.engine;
|
|
29456
|
+
if (!m.getLayer(id)) { return; }
|
|
29457
|
+
Object.keys(e.paint || {}).forEach(function (k) { m.setPaintProperty(id, k, e.paint[k]); });
|
|
29458
|
+
Object.keys(e.layout || {}).forEach(function (k) { m.setLayoutProperty(id, k, e.layout[k]); });
|
|
29459
|
+
if (e.filter !== undefined) { m.setFilter(id, e.filter); }
|
|
29460
|
+
};
|
|
29461
|
+
|
|
29462
|
+
/** A style layer as a Style Specification object (a copy): yours, or the basemap's; null when there is none. */
|
|
29463
|
+
SMap.prototype.getStyleLayer = function (layerId) {
|
|
29464
|
+
var def = this._layers[layerId];
|
|
29465
|
+
if (def && def.kind === 'style') { return clone(def.layer); }
|
|
29466
|
+
if (def) { return null; }
|
|
29467
|
+
var st = this.engine && this.engine.getStyle && this.engine.getStyle();
|
|
29468
|
+
var found = st && st.layers ? st.layers.filter(function (l) { return l.id === layerId; })[0] : null;
|
|
29469
|
+
return found ? clone(found) : null;
|
|
29470
|
+
};
|
|
29471
|
+
|
|
29472
|
+
// ------------------------------------------------------------------ basemap options (M26)
|
|
29473
|
+
|
|
29474
|
+
// The basemap's layers as the style came (before any override): ids in order and, for layers of the class
|
|
29475
|
+
// table, their paint and layout.
|
|
29476
|
+
SMap.prototype._snapshotBasemap = function () {
|
|
29477
|
+
var m = this.engine;
|
|
29478
|
+
var st = m.getStyle && m.getStyle();
|
|
29479
|
+
var layers = st && Array.isArray(st.layers) ? st.layers : [];
|
|
29480
|
+
var index = interop.basemapLayerIndex();
|
|
29481
|
+
var mine = {}, self = this;
|
|
29482
|
+
this._order.forEach(function (id) { (self._layers[id].engineLayers || []).forEach(function (l) { mine[l] = true; }); });
|
|
29483
|
+
this._basemapIds = layers.map(function (l) { return l.id; }).filter(function (id) { return !mine[id] && id !== BUILDINGS_LAYER; });
|
|
29484
|
+
this._basemapOriginal = {};
|
|
29485
|
+
this._basemapApplied = {};
|
|
29486
|
+
layers.forEach(function (l) {
|
|
29487
|
+
if (index[l.id]) { self._basemapOriginal[l.id] = { id: l.id, paint: clone(l.paint) || {}, layout: clone(l.layout) || {} }; }
|
|
29488
|
+
});
|
|
29489
|
+
};
|
|
29490
|
+
|
|
29491
|
+
// Sets on the engine what the overrides change (and back what they no longer change).
|
|
29492
|
+
SMap.prototype._applyBasemap = function () {
|
|
29493
|
+
var m = this.engine, self = this;
|
|
29494
|
+
var ids = Object.keys(this._basemapOriginal);
|
|
29495
|
+
if (!ids.length) { return; }
|
|
29496
|
+
var styled = interop.applyBasemapOverrides({ layers: ids.map(function (id) { return self._basemapOriginal[id]; }) }, this._basemapOverrides);
|
|
29497
|
+
styled.layers.forEach(function (l) {
|
|
29498
|
+
var before = self._basemapApplied[l.id] || self._basemapOriginal[l.id];
|
|
29499
|
+
var same = function (a, b) { return JSON.stringify(a) === JSON.stringify(b); };
|
|
29500
|
+
var keys = function (a, b) { var k = {}; Object.keys(a || {}).concat(Object.keys(b || {})).forEach(function (x) { k[x] = true; }); return Object.keys(k); };
|
|
29501
|
+
if (!m.getLayer(l.id)) { return; }
|
|
29502
|
+
keys(before.paint, l.paint).forEach(function (k) { if (!same(before.paint[k], l.paint[k])) { m.setPaintProperty(l.id, k, l.paint[k]); } });
|
|
29503
|
+
keys(before.layout, l.layout).forEach(function (k) { if (!same(before.layout[k], l.layout[k])) { m.setLayoutProperty(l.id, k, l.layout[k]); } });
|
|
29504
|
+
self._basemapApplied[l.id] = l;
|
|
29505
|
+
});
|
|
29506
|
+
};
|
|
29507
|
+
|
|
29508
|
+
/**
|
|
29509
|
+
* Shows, hides and recolours the basemap by class (`water`, `land`, `parks`, `roads` and `roads.highway` … ,
|
|
29510
|
+
* `labels` and `labels.places` …, `pois`, …): {class: {visible, color, casing, halo, opacity, width}}. Replaces the
|
|
29511
|
+
* overrides set before ({} clears them); kept across style and language changes. An unknown class or field throws.
|
|
29512
|
+
*/
|
|
29513
|
+
SMap.prototype.setBasemap = function (overrides) {
|
|
29514
|
+
interop.overrideOrder(overrides || {});
|
|
29515
|
+
this._basemapOverrides = clone(overrides || {});
|
|
29516
|
+
if (this.engine && this._styleReady) { this._applyBasemap(); }
|
|
29517
|
+
return this;
|
|
29518
|
+
};
|
|
29519
|
+
|
|
29520
|
+
/** The basemap overrides in force (a copy). */
|
|
29521
|
+
SMap.prototype.basemap = function () { return clone(this._basemapOverrides); };
|
|
29522
|
+
|
|
29523
|
+
// ------------------------------------------------------------------ files (M28–M30)
|
|
29524
|
+
|
|
29525
|
+
function isFileLike(v) { return !!v && typeof v === 'object' && typeof v.arrayBuffer === 'function' && !(v instanceof Uint8Array); }
|
|
29526
|
+
|
|
29527
|
+
// A popup from simplestyle properties (title or name, description), HTML-escaped; nothing when a feature has neither.
|
|
29528
|
+
function simplestylePopup(p) {
|
|
29529
|
+
var title = p.title !== undefined && p.title !== null && p.title !== '' ? p.title : p.name;
|
|
29530
|
+
var desc = p.description !== undefined && p.description !== null && p.description !== '' ? p.description : p.desc;
|
|
29531
|
+
if ((title === undefined || title === null || title === '') && (desc === undefined || desc === null || desc === '')) { return null; }
|
|
29532
|
+
return '<div class="smap-simplestyle">' + (title !== undefined && title !== null && title !== '' ? '<b>' + escapeHtml(title) + '</b>' : '') +
|
|
29533
|
+
(desc !== undefined && desc !== null && desc !== '' ? (title ? '<br>' : '') + escapeHtml(desc) : '') + '</div>';
|
|
29534
|
+
}
|
|
29535
|
+
|
|
29536
|
+
var FILE_OPTIONS = ['format', 'lat', 'lng', 'name'];
|
|
29537
|
+
|
|
29538
|
+
/**
|
|
29539
|
+
* Reads a file (KML, KMZ, GPX, encoded polyline, WKT, WKB, CSV, TopoJSON, GeoJSON) and draws it: its points as the
|
|
29540
|
+
* layer `<id>`, its lines as `<id>:lines`, its areas as `<id>:areas` (only those it has), with simplestyle
|
|
29541
|
+
* properties drawn when no colour is given, and a popup of each feature's title / name and description unless
|
|
29542
|
+
* `popup` is given. `removeLayer(id)` removes all three, `toGeoJSON(id)` gives them back together.
|
|
29543
|
+
* @param input text, bytes, a parsed object, or a File / Blob (format from its name when not given)
|
|
29544
|
+
* @param o {format, lat, lng, ...the options of addPoints / addLines / addPolygons}
|
|
29545
|
+
* @returns Promise of the map, once drawn (rejected when the file cannot be read)
|
|
29546
|
+
*/
|
|
29547
|
+
SMap.prototype.addData = function (id, input, o) {
|
|
29548
|
+
var self = this;
|
|
29549
|
+
o = o || {};
|
|
29550
|
+
var read = isFileLike(input) ? interop.formats.readFile(input, o) : new Promise(function (resolve) { resolve(interop.formats.read(input, o)); });
|
|
29551
|
+
return read.then(function (data) { self._addFeatures(id, data, o); return self; });
|
|
29552
|
+
};
|
|
29553
|
+
|
|
29554
|
+
SMap.prototype._addFeatures = function (id, data, o) {
|
|
29555
|
+
var points = [], lines = [], areas = [];
|
|
29556
|
+
var place = function (f, g) {
|
|
29557
|
+
if (!g) { return; }
|
|
29558
|
+
if (g.type === 'GeometryCollection') { (g.geometries || []).forEach(function (x) { place(f, x); }); return; }
|
|
29559
|
+
var part = { type: 'Feature', properties: f.properties || {}, geometry: g };
|
|
29560
|
+
if (f.id !== undefined) { part.id = f.id; }
|
|
29561
|
+
if (g.type === 'Point' || g.type === 'MultiPoint') { points.push(part); }
|
|
29562
|
+
else if (g.type === 'LineString' || g.type === 'MultiLineString') { lines.push(part); }
|
|
29563
|
+
else if (g.type === 'Polygon' || g.type === 'MultiPolygon') { areas.push(part); }
|
|
29564
|
+
};
|
|
29565
|
+
(data.features || []).forEach(function (f) { place(f, f.geometry); });
|
|
29566
|
+
this.removeLayer(id);
|
|
29567
|
+
var opts = {};
|
|
29568
|
+
Object.keys(o).forEach(function (k) { if (FILE_OPTIONS.indexOf(k) < 0) { opts[k] = o[k]; } });
|
|
29569
|
+
if (opts.popup === undefined) { opts.popup = simplestylePopup; }
|
|
29570
|
+
var fcOf = function (f) { return { type: 'FeatureCollection', features: f }; };
|
|
29571
|
+
if (areas.length) { this.addPolygons(id + ':areas', fcOf(areas), opts); this._layers[id + ':areas'].group = id; }
|
|
29572
|
+
if (lines.length) { this.addLines(id + ':lines', fcOf(lines), opts); this._layers[id + ':lines'].group = id; }
|
|
29573
|
+
if (points.length) { this.addPoints(id, fcOf(points), opts); this._layers[id].group = id; }
|
|
29574
|
+
};
|
|
29575
|
+
|
|
29576
|
+
/** A layer's features as GeoJSON (a copy): an SDK layer, an addData group, or a style layer of a GeoJSON source. */
|
|
29577
|
+
SMap.prototype.toGeoJSON = function (layerId) {
|
|
29578
|
+
var self = this;
|
|
29579
|
+
var members = this._order.filter(function (x) { return self._layers[x].group === layerId; });
|
|
29580
|
+
if (members.length) {
|
|
29581
|
+
var features = [];
|
|
29582
|
+
[layerId + ':areas', layerId + ':lines', layerId].forEach(function (x) { if (members.indexOf(x) >= 0) { features = features.concat(clone(self._layers[x].data.features)); } });
|
|
29583
|
+
return { type: 'FeatureCollection', features: features };
|
|
29584
|
+
}
|
|
29585
|
+
var def = this._layers[layerId];
|
|
29586
|
+
if (def && def.kind !== 'style') { return clone(def.data); }
|
|
29587
|
+
var source = def ? (typeof def.layer.source === 'object' ? def.layer.source : this._sources[def.layer.source]) : this._sources[layerId];
|
|
29588
|
+
if (source && source.type === 'geojson' && source.data && typeof source.data === 'object') { return interop.formats.read(clone(source.data), { format: 'geojson' }); }
|
|
29589
|
+
throw new Error('sMap: ' + layerId + ' has no GeoJSON features to give');
|
|
29590
|
+
};
|
|
29591
|
+
|
|
29592
|
+
/** A layer's features as a KML document (text). */
|
|
29593
|
+
SMap.prototype.toKML = function (layerId) { return interop.formats.toKML(this.toGeoJSON(layerId), { name: layerId }); };
|
|
29594
|
+
|
|
29595
|
+
/** Converts a file on sMap (POST /v1/convert) with this map's address, key and token: Promise of GeoJSON. */
|
|
29596
|
+
SMap.prototype.convert = function (input, o) {
|
|
29597
|
+
return interop.convert(input, Object.assign({ baseUrl: this._base, apiKey: this._apiKey, token: this._token }, o || {}));
|
|
29598
|
+
};
|
|
29599
|
+
|
|
29600
|
+
/**
|
|
29601
|
+
* sMap's raster tiles of a style as a `{z}/{x}/{y}` template on this map's node and key (M35), for tools without
|
|
29602
|
+
* vector tiles. style: a scheme or `custom:<id>` (default: the map's own); o: {lang (default the map's language),
|
|
29603
|
+
* retina}.
|
|
29604
|
+
*/
|
|
29605
|
+
SMap.prototype.rasterUrl = function (style, o) {
|
|
29606
|
+
o = o || {};
|
|
29607
|
+
var name = style || this._serverStyle || this._flavor;
|
|
29608
|
+
return interop.rasterUrl(name, { baseUrl: this._base, apiKey: this._apiKey || undefined, lang: o.lang || o.language || this._language, retina: !!o.retina });
|
|
29609
|
+
};
|
|
29610
|
+
|
|
29611
|
+
// ------------------------------------------------------------------ external layers (M31)
|
|
29612
|
+
|
|
29613
|
+
var LAYER_PLACE = ['slot', 'before', 'zIndex'];
|
|
29614
|
+
function placeOf(o) { var p = {}; LAYER_PLACE.forEach(function (k) { if (o[k] !== undefined) { p[k] = o[k]; } }); return p; }
|
|
29615
|
+
function copyKeys(from, to, keys) { keys.forEach(function (k) { if (from[k] !== undefined) { to[k] = from[k]; } }); return to; }
|
|
29616
|
+
|
|
29617
|
+
SMap.prototype._addRaster = function (id, source, o) {
|
|
29618
|
+
this.addSource(id, source);
|
|
29619
|
+
var layer = copyKeys(o, { id: id, type: 'raster', source: id }, ['minzoom', 'maxzoom']);
|
|
29620
|
+
if (o.opacity !== undefined) { layer.paint = { 'raster-opacity': o.opacity }; }
|
|
29621
|
+
this.addStyleLayer(layer, placeOf(o));
|
|
29622
|
+
this._layers[id].ownsSource = id;
|
|
29623
|
+
return this;
|
|
29624
|
+
};
|
|
29625
|
+
|
|
29626
|
+
/**
|
|
29627
|
+
* Raster tiles from any XYZ template (`tiles` or `url` with {z}/{x}/{y}) or a TileJSON address (`url`).
|
|
29628
|
+
* @param o {tiles, url, tileSize (256), attribution, minzoom, maxzoom, bounds, scheme, opacity, slot, before, zIndex}
|
|
29629
|
+
*/
|
|
29630
|
+
SMap.prototype.addRasterLayer = function (id, o) {
|
|
29631
|
+
o = o || {};
|
|
29632
|
+
var source = { type: 'raster', tileSize: o.tileSize || 256 };
|
|
29633
|
+
if (o.tiles) { source.tiles = Array.isArray(o.tiles) ? o.tiles.slice() : [o.tiles]; }
|
|
29634
|
+
else if (o.url && /\{z\}/.test(o.url)) { source.tiles = [o.url]; }
|
|
29635
|
+
else if (o.url) { source.url = o.url; }
|
|
29636
|
+
else { throw new Error('sMap: addRasterLayer needs tiles or url'); }
|
|
29637
|
+
copyKeys(o, source, ['attribution', 'minzoom', 'maxzoom', 'bounds', 'scheme']);
|
|
29638
|
+
return this._addRaster(id, source, o);
|
|
29639
|
+
};
|
|
29640
|
+
|
|
29641
|
+
/**
|
|
29642
|
+
* An OGC WMS layer, one GetMap per tile in Web Mercator.
|
|
29643
|
+
* @param o {url, layers, styles, format ('image/png'), transparent (true), version ('1.3.0' | '1.1.1'), params,
|
|
29644
|
+
* tileSize, attribution, opacity, minzoom, maxzoom, slot, before, zIndex}
|
|
29645
|
+
*/
|
|
29646
|
+
SMap.prototype.addWmsLayer = function (id, o) {
|
|
29647
|
+
o = o || {};
|
|
29648
|
+
var source = copyKeys(o, { type: 'raster', tiles: [interop.wmsTileUrl(o)], tileSize: o.tileSize || 256 }, ['attribution', 'minzoom', 'maxzoom', 'bounds']);
|
|
29649
|
+
return this._addRaster(id, source, o);
|
|
29650
|
+
};
|
|
29651
|
+
|
|
29652
|
+
/**
|
|
29653
|
+
* An OGC WMTS layer (Web Mercator matrix sets): from the GetCapabilities address (`url`, or its text as
|
|
29654
|
+
* `capabilities`), KVP or REST; or from a REST template with {TileMatrix}, {TileRow}, {TileCol} (`url`).
|
|
29655
|
+
* @param o {url, capabilities, layer, tileMatrixSet, style, format, encoding ('rest' | 'kvp'), attribution, opacity,
|
|
29656
|
+
* minzoom, maxzoom, slot, before, zIndex}
|
|
29657
|
+
* @returns Promise of the map, once added
|
|
29658
|
+
*/
|
|
29659
|
+
SMap.prototype.addWmtsLayer = function (id, o) {
|
|
29660
|
+
var self = this;
|
|
29661
|
+
o = o || {};
|
|
29662
|
+
var url = o.url ? String(o.url) : '';
|
|
29663
|
+
var add = function (source) { copyKeys(o, source, ['attribution', 'minzoom', 'maxzoom']); return self._addRaster(id, source, o); };
|
|
29664
|
+
try {
|
|
29665
|
+
if (/\{TileMatrix\}|\{TileRow\}|\{z\}/i.test(url)) {
|
|
29666
|
+
var tile = url.replace(/\{TileMatrixSet\}/gi, o.tileMatrixSet || 'GoogleMapsCompatible').replace(/\{TileMatrix\}/gi, (o.matrixPrefix || '') + '{z}')
|
|
29667
|
+
.replace(/\{TileRow\}/gi, '{y}').replace(/\{TileCol\}/gi, '{x}').replace(/\{Style\}/gi, o.style || 'default');
|
|
29668
|
+
interop.refuseForbidden([tile]);
|
|
29669
|
+
return Promise.resolve(add({ type: 'raster', tiles: [tile], tileSize: o.tileSize || 256 }));
|
|
29670
|
+
}
|
|
29671
|
+
if (o.capabilities) { return Promise.resolve(add(interop.wmtsSource(o.capabilities, o))); }
|
|
29672
|
+
} catch (e) { return Promise.reject(e); }
|
|
29673
|
+
if (!url) { return Promise.reject(new Error('sMap: addWmtsLayer needs url or capabilities')); }
|
|
29674
|
+
var capsUrl = /request=getcapabilities/i.test(url) || /\.xml(\?|$)/i.test(url) ? url : url + (url.indexOf('?') < 0 ? '?' : '&') + 'SERVICE=WMTS&REQUEST=GetCapabilities';
|
|
29675
|
+
try { interop.refuseForbidden([capsUrl]); } catch (e2) { return Promise.reject(e2); }
|
|
29676
|
+
return fetch(capsUrl).then(function (res) {
|
|
29677
|
+
if (!res.ok) { throw new Error('sMap: WMTS capabilities: HTTP ' + res.status); }
|
|
29678
|
+
return res.text();
|
|
29679
|
+
}).then(function (xml) { return add(interop.wmtsSource(xml, Object.assign({}, o, { url: url.split('?')[0] }))); });
|
|
29680
|
+
};
|
|
29681
|
+
|
|
29682
|
+
/**
|
|
29683
|
+
* Vector tiles (Mapbox Vector Tiles) from any address: `tiles` (templates), `url` (TileJSON, or a PMTiles archive
|
|
29684
|
+
* ending in .pmtiles) or `pmtiles` (an archive address); drawn by your style layers (`layers`, their `source` set to
|
|
29685
|
+
* this id when they name none).
|
|
29686
|
+
* @param o {tiles, url, pmtiles, layers, attribution, minzoom, maxzoom, bounds, promoteId, slot, before, zIndex}
|
|
29687
|
+
*/
|
|
29688
|
+
SMap.prototype.addVectorTiles = function (id, o) {
|
|
29689
|
+
o = o || {};
|
|
29690
|
+
var source = { type: 'vector' };
|
|
29691
|
+
var archive = o.pmtiles || (o.url && (/^pmtiles:\/\//i.test(o.url) || /\.pmtiles(\?|$)/i.test(o.url)) ? o.url : null);
|
|
29692
|
+
if (archive) { source.url = /^pmtiles:\/\//i.test(archive) ? archive : 'pmtiles://' + archive; }
|
|
29693
|
+
else if (o.tiles) { source.tiles = Array.isArray(o.tiles) ? o.tiles.slice() : [o.tiles]; }
|
|
29694
|
+
else if (o.url) { source.url = o.url; }
|
|
29695
|
+
else { throw new Error('sMap: addVectorTiles needs tiles, url or pmtiles'); }
|
|
29696
|
+
copyKeys(o, source, ['attribution', 'minzoom', 'maxzoom', 'bounds', 'promoteId', 'scheme']);
|
|
29697
|
+
this.addSource(id, source);
|
|
29698
|
+
var self = this, place = placeOf(o);
|
|
29699
|
+
(o.layers || []).forEach(function (l) {
|
|
29700
|
+
var layer = clone(l);
|
|
29701
|
+
if (!layer.source) { layer.source = id; }
|
|
29702
|
+
self.addStyleLayer(layer, place);
|
|
29703
|
+
});
|
|
29704
|
+
return this;
|
|
29705
|
+
};
|
|
29706
|
+
|
|
29707
|
+
/**
|
|
29708
|
+
* An ArcGIS REST service: a MapServer / ImageServer tile service (raster) or a VectorTileServer (vector tiles,
|
|
29709
|
+
* drawn by `layers` or else by the service's own root style). The service's copyright text is its attribution
|
|
29710
|
+
* unless `attribution` is given.
|
|
29711
|
+
* @param o {url, layers, attribution, opacity, minzoom, maxzoom, slot, before, zIndex}
|
|
29712
|
+
* @returns Promise of the map, once added
|
|
29713
|
+
*/
|
|
29714
|
+
SMap.prototype.addArcGisLayer = function (id, o) {
|
|
29715
|
+
var self = this;
|
|
29716
|
+
o = o || {};
|
|
29717
|
+
if (!o.url) { return Promise.reject(new Error('sMap: addArcGisLayer needs url')); }
|
|
29718
|
+
var url = String(o.url).split('?')[0].replace(/\/+$/, '');
|
|
29719
|
+
try { interop.refuseForbidden([url]); } catch (e) { return Promise.reject(e); }
|
|
29720
|
+
var vector = /\/VectorTileServer$/i.test(url);
|
|
29721
|
+
if (!vector && !/\/(MapServer|ImageServer)$/i.test(url)) { return Promise.reject(new Error('sMap: an ArcGIS address ends in /MapServer, /ImageServer or /VectorTileServer')); }
|
|
29722
|
+
var json = function (u) { return typeof fetch === 'undefined' ? Promise.resolve({}) : fetch(u).then(function (r) { return r.ok ? r.json() : {}; }).catch(function () { return {}; }); };
|
|
29723
|
+
return json(url + '?f=json').then(function (info) {
|
|
29724
|
+
var attribution = o.attribution || (info && info.copyrightText) || undefined;
|
|
29725
|
+
var opts = Object.assign({}, o, { attribution: attribution });
|
|
29726
|
+
if (!vector) {
|
|
29727
|
+
var tileSize = info && info.tileInfo && info.tileInfo.rows ? info.tileInfo.rows : 256;
|
|
29728
|
+
var source = copyKeys(opts, { type: 'raster', tiles: [url + '/tile/{z}/{y}/{x}'], tileSize: tileSize }, ['attribution', 'minzoom', 'maxzoom']);
|
|
29729
|
+
return self._addRaster(id, source, opts);
|
|
29730
|
+
}
|
|
29731
|
+
var layers = o.layers ? Promise.resolve(o.layers) : json(url + '/' + ((info && info.defaultStyles) || 'resources/styles') + '/root.json').then(function (st) { return (st && st.layers) || []; });
|
|
29732
|
+
return layers.then(function (ls) {
|
|
29733
|
+
var tiles = info && Array.isArray(info.tiles) && info.tiles.length ? info.tiles.map(function (t) { return /^https?:/.test(t) ? t : url + '/' + t; }) : [url + '/tile/{z}/{y}/{x}.pbf'];
|
|
29734
|
+
self.addVectorTiles(id, Object.assign({}, opts, {
|
|
29735
|
+
tiles: tiles,
|
|
29736
|
+
layers: ls.map(function (l) { var c = clone(l); if (c.source) { c.source = id; } return c; }),
|
|
29737
|
+
}));
|
|
29738
|
+
return self;
|
|
29739
|
+
});
|
|
29740
|
+
});
|
|
29741
|
+
};
|
|
29742
|
+
|
|
29743
|
+
// ------------------------------------------------------------------ deck.gl layers (M36, web only)
|
|
29744
|
+
|
|
29745
|
+
/**
|
|
29746
|
+
* deck.gl's map overlay (MapboxOverlay of @deck.gl/mapbox 9.x) reads the engine map's camera as `map.transform`
|
|
29747
|
+
* (its height, elevation, _nearZ and _farZ) on every frame; the bundled engine keeps it at `map._camera.transform`.
|
|
29748
|
+
* Gives the engine map a read-only `transform` that follows the camera, only when the engine has none of its own.
|
|
29749
|
+
* Every other member the overlay reads (painter.context.gl, style._loaded / _order, getProjection, getTerrain,
|
|
29750
|
+
* getRenderWorldCopies, getPadding, the camera getters, add / move / remove / getLayer, on / off, triggerRepaint,
|
|
29751
|
+
* getCanvas / getContainer, removeControl) is the engine's own.
|
|
29752
|
+
*/
|
|
29753
|
+
function deckCompat(map) {
|
|
29754
|
+
if (!map || typeof map !== 'object' || 'transform' in map) { return map; }
|
|
29755
|
+
if (!map._camera || typeof map._camera !== 'object' || !('transform' in map._camera)) { return map; }
|
|
29756
|
+
try {
|
|
29757
|
+
Object.defineProperty(map, 'transform', {
|
|
29758
|
+
configurable: true, enumerable: false,
|
|
29759
|
+
get: function () { return this._camera && this._camera.transform; }
|
|
29760
|
+
});
|
|
29761
|
+
} catch (e) { /* a frozen engine map keeps what it has */ }
|
|
29762
|
+
return map;
|
|
29763
|
+
}
|
|
29764
|
+
|
|
29765
|
+
/**
|
|
29766
|
+
* Draws a deck.gl layer interleaved with the basemap, through deck.gl's map overlay. deck.gl is optional: load it
|
|
29767
|
+
* once with sMap.useDeck(await import('@deck.gl/mapbox')), or the global `deck` of its script build is used.
|
|
29768
|
+
* A layer with the same id replaces the one before. o: {slot, before} places it (deck.gl's beforeId).
|
|
29769
|
+
*/
|
|
29770
|
+
SMap.prototype.addDeckLayer = function (layer, o) {
|
|
29771
|
+
var self = this;
|
|
29772
|
+
if (!this.engine) { this.ready.then(function () { self.addDeckLayer(layer, o); }); return this; }
|
|
29773
|
+
var d = interop.deck();
|
|
29774
|
+
if (!d) { throw new Error('sMap: deck.gl is not loaded; call sMap.useDeck(await import("@deck.gl/mapbox")) first'); }
|
|
29775
|
+
if (!layer || !layer.id) { throw new Error('sMap: addDeckLayer needs a deck.gl layer with an id'); }
|
|
29776
|
+
o = o || {};
|
|
29777
|
+
if ((o.slot || o.before) && typeof layer.clone === 'function') {
|
|
29778
|
+
var anchor = o.before ? (this._layers[o.before] ? this._firstEngineLayer(o.before) : o.before) : this._groupAnchor(o.slot);
|
|
29779
|
+
if (anchor) { layer = layer.clone({ beforeId: anchor }); }
|
|
29780
|
+
}
|
|
29781
|
+
this._deckLayers = this._deckLayers.filter(function (l) { return l.id !== layer.id; }).concat([layer]);
|
|
29782
|
+
if (!this._deckOverlay) {
|
|
29783
|
+
deckCompat(this.engine);
|
|
29784
|
+
this._deckOverlay = new d.MapboxOverlay({ interleaved: true, layers: this._deckLayers.slice() });
|
|
29785
|
+
this.engine.addControl(this._deckOverlay);
|
|
29786
|
+
} else {
|
|
29787
|
+
this._deckOverlay.setProps({ layers: this._deckLayers.slice() });
|
|
29788
|
+
}
|
|
29789
|
+
return this;
|
|
29790
|
+
};
|
|
29791
|
+
|
|
29792
|
+
/**
|
|
29793
|
+
* The map's deck.gl overlay (deck.gl's MapboxOverlay, interleaved with the basemap; one per map), made on first use.
|
|
29794
|
+
* `props` are other deck.gl props of the overlay (getTooltip, onClick, pickingRadius, …); its layers stay the ones
|
|
29795
|
+
* added with addDeckLayer (a `layers` prop is ignored). Returns null before the map is ready or when deck.gl is not
|
|
29796
|
+
* loaded (sMap.useDeck).
|
|
29797
|
+
*/
|
|
29798
|
+
SMap.prototype.deckOverlay = function (props) {
|
|
29799
|
+
if (!this.engine) { return null; }
|
|
29800
|
+
if (!this._deckOverlay) {
|
|
29801
|
+
var d = interop.deck();
|
|
29802
|
+
if (!d) { return null; }
|
|
29803
|
+
deckCompat(this.engine);
|
|
29804
|
+
this._deckOverlay = new d.MapboxOverlay({ interleaved: true, layers: this._deckLayers.slice() });
|
|
29805
|
+
this.engine.addControl(this._deckOverlay);
|
|
29806
|
+
}
|
|
29807
|
+
if (props && typeof props === 'object') {
|
|
29808
|
+
var own = {};
|
|
29809
|
+
Object.keys(props).forEach(function (k) { if (k !== 'layers' && k !== 'interleaved') { own[k] = props[k]; } });
|
|
29810
|
+
if (Object.keys(own).length) { this._deckOverlay.setProps(own); }
|
|
29811
|
+
}
|
|
29812
|
+
return this._deckOverlay;
|
|
29813
|
+
};
|
|
29814
|
+
|
|
29815
|
+
/** Removes a deck.gl layer by its id. */
|
|
29816
|
+
SMap.prototype.removeDeckLayer = function (id) {
|
|
29817
|
+
this._deckLayers = this._deckLayers.filter(function (l) { return l.id !== id; });
|
|
29818
|
+
if (this._deckOverlay) { this._deckOverlay.setProps({ layers: this._deckLayers.slice() }); }
|
|
29819
|
+
return this;
|
|
29820
|
+
};
|
|
29821
|
+
|
|
29822
|
+
/** The deck.gl layers on the map. */
|
|
29823
|
+
SMap.prototype.deckLayers = function () { return this._deckLayers.slice(); };
|
|
29824
|
+
|
|
29825
|
+
// ------------------------------------------------------------------ layer description (M32, shared/LAYER-JSON.md)
|
|
29826
|
+
|
|
29827
|
+
var NOT_OPTIONS = ['slot', 'before', 'zIndex'];
|
|
29828
|
+
|
|
29829
|
+
/** The map's layers as the shared layer description (LAYER-JSON.md): basemap, sources, layers. */
|
|
29830
|
+
SMap.prototype.toJSON = function () {
|
|
29831
|
+
var self = this;
|
|
29832
|
+
var out = { type: 'smap.layers', version: 1 };
|
|
29833
|
+
if (!this._customStyle) { out.basemap = { style: this._serverStyle || this._flavor, language: this._language, overrides: clone(this._basemapOverrides) }; }
|
|
29834
|
+
if (this._sourceOrder.length) { out.sources = this.sources(); }
|
|
29835
|
+
out.layers = this._order.map(function (id) {
|
|
29836
|
+
var def = self._layers[id];
|
|
29837
|
+
var e = { id: id, kind: def.kind };
|
|
29838
|
+
if (def.slot) { e.slot = def.slot; }
|
|
29839
|
+
if (def.before) { e.before = def.before; }
|
|
29840
|
+
if (def.zIndex) { e.zIndex = def.zIndex; }
|
|
29841
|
+
if (def.hidden) { e.visible = false; }
|
|
29842
|
+
if (def.group) { e.group = def.group; }
|
|
29843
|
+
if (def.kind === 'style') { e.layer = clone(def.layer); return e; }
|
|
29844
|
+
e.data = clone(def.data);
|
|
29845
|
+
var options = {};
|
|
29846
|
+
Object.keys(def.o).forEach(function (k) { if (NOT_OPTIONS.indexOf(k) < 0 && !(k === 'popup' && typeof def.o[k] !== 'string')) { options[k] = def.o[k]; } });
|
|
29847
|
+
e.options = interop.jsonSafe(options);
|
|
29848
|
+
return e;
|
|
29849
|
+
});
|
|
29850
|
+
return out;
|
|
29851
|
+
};
|
|
29852
|
+
|
|
29853
|
+
/**
|
|
29854
|
+
* Loads a layer description (LAYER-JSON.md, an object or its text): its basemap, sources and layers, each through
|
|
29855
|
+
* the call that made it; an id already on the map is replaced, nothing else changes.
|
|
29856
|
+
*/
|
|
29857
|
+
SMap.prototype.loadLayers = function (json) {
|
|
29858
|
+
var self = this;
|
|
29859
|
+
json = interop.checkLayerJson(json);
|
|
29860
|
+
var b = json.basemap;
|
|
29861
|
+
if (b) {
|
|
29862
|
+
var style = b.style && b.style !== (this._serverStyle || this._flavor) ? b.style : null;
|
|
29863
|
+
if (b.language) { this._language = b.language; }
|
|
29864
|
+
if (style && this.engine) { this.setStyle(style); } else if (style) { this._takeStyle(style); }
|
|
29865
|
+
else if (b.language && this.engine && !this._customStyle) { this.setLanguage(b.language); }
|
|
29866
|
+
if (b.overrides) { this.setBasemap(b.overrides); }
|
|
29867
|
+
}
|
|
29868
|
+
Object.keys(json.sources || {}).forEach(function (id) { self.addSource(id, clone(json.sources[id])); });
|
|
29869
|
+
json.layers.forEach(function (e) {
|
|
29870
|
+
var place = {};
|
|
29871
|
+
if (e.slot) { place.slot = e.slot; }
|
|
29872
|
+
if (e.before) { place.before = e.before; }
|
|
29873
|
+
if (e.zIndex) { place.zIndex = e.zIndex; }
|
|
29874
|
+
if (e.kind === 'style') { self.addStyleLayer(e.layer, place); } else {
|
|
29875
|
+
var opts = Object.assign({}, clone(e.options) || {}, place);
|
|
29876
|
+
if (e.kind === 'points') { self.addPoints(e.id, e.data, opts); }
|
|
29877
|
+
else if (e.kind === 'lines') { self.addLines(e.id, e.data, opts); }
|
|
29878
|
+
else { self.addPolygons(e.id, e.data, opts); }
|
|
29879
|
+
}
|
|
29880
|
+
if (e.group && self._layers[e.id]) { self._layers[e.id].group = e.group; }
|
|
29881
|
+
if (e.visible === false) { self.setVisible(e.id, false); }
|
|
29882
|
+
});
|
|
29883
|
+
return this;
|
|
29884
|
+
};
|
|
29885
|
+
|
|
27800
29886
|
SMap.prototype._define = function (id, def) {
|
|
27801
29887
|
if (this._layers[id]) { this._drop(id, this._layers[id]); }
|
|
29888
|
+
var o = def.o;
|
|
29889
|
+
if (o.slot !== undefined && o.slot !== null && SLOTS.indexOf(o.slot) < 0) { throw new Error('sMap: unknown slot "' + o.slot + '" (bottom, roads, labels, top)'); }
|
|
29890
|
+
if (o.slot && o.before) { throw new Error('sMap: give a layer either slot or before'); }
|
|
29891
|
+
def.slot = o.slot || null;
|
|
29892
|
+
def.before = o.before || null;
|
|
29893
|
+
def.popupFn = typeof o.popup === 'string' ? popupTemplate(o.popup) : null;
|
|
27802
29894
|
def.zIndex = def.o.zIndex || 0;
|
|
27803
29895
|
def.engineLayers = [];
|
|
27804
29896
|
this._layers[id] = def;
|
|
@@ -27817,21 +29909,49 @@ function smapCreateSdk(engine) {
|
|
|
27817
29909
|
return undefined;
|
|
27818
29910
|
};
|
|
27819
29911
|
|
|
27820
|
-
//
|
|
29912
|
+
// Layers are drawn in groups: a slot, or under one named layer (`before`). A layer without either draws as in 1.0,
|
|
29913
|
+
// on top, in the group of slot `top`.
|
|
29914
|
+
function groupOf(def) { return def.before ? 'before:' + def.before : (def.slot || 'top'); }
|
|
29915
|
+
|
|
29916
|
+
// The layer to insert before: the next layer of the same group (so a higher zIndex always draws on top), else
|
|
29917
|
+
// where the group begins.
|
|
27821
29918
|
SMap.prototype._beforeId = function (id) {
|
|
27822
|
-
var at = this._order.indexOf(id);
|
|
29919
|
+
var at = this._order.indexOf(id), g = groupOf(this._layers[id]);
|
|
27823
29920
|
for (var i = at + 1; i < this._order.length; i++) {
|
|
29921
|
+
if (groupOf(this._layers[this._order[i]]) !== g) { continue; }
|
|
27824
29922
|
var first = this._firstEngineLayer(this._order[i]);
|
|
27825
29923
|
if (first) { return first; }
|
|
27826
29924
|
}
|
|
27827
|
-
return
|
|
29925
|
+
return this._groupAnchor(g);
|
|
29926
|
+
};
|
|
29927
|
+
|
|
29928
|
+
// Where a group begins: under a named layer (the SDK id of a layer, or any engine layer id), or the slot's first
|
|
29929
|
+
// basemap layer (from the class table, so it stays put when the basemap's layer list changes).
|
|
29930
|
+
SMap.prototype._groupAnchor = function (g) {
|
|
29931
|
+
var m = this.engine;
|
|
29932
|
+
if (g === 'top') { return undefined; }
|
|
29933
|
+
if (g.indexOf('before:') === 0) {
|
|
29934
|
+
var target = g.slice(7);
|
|
29935
|
+
if (this._layers[target]) { return this._firstEngineLayer(target); }
|
|
29936
|
+
return m.getLayer && m.getLayer(target) ? target : undefined;
|
|
29937
|
+
}
|
|
29938
|
+
var present = this._basemapIds.filter(function (x) { return m.getLayer && m.getLayer(x); });
|
|
29939
|
+
var anchor = interop.slotAnchors(present)[g];
|
|
29940
|
+
return anchor && m.getLayer(anchor) ? anchor : undefined;
|
|
29941
|
+
};
|
|
29942
|
+
|
|
29943
|
+
/** Where each slot begins in the current basemap: {bottom, roads, labels, top} as layer ids (top: undefined). */
|
|
29944
|
+
SMap.prototype.slots = function () {
|
|
29945
|
+
var m = this.engine;
|
|
29946
|
+
var present = this._basemapIds.filter(function (x) { return m && m.getLayer && m.getLayer(x); });
|
|
29947
|
+
return interop.slotAnchors(present);
|
|
27828
29948
|
};
|
|
27829
29949
|
|
|
27830
29950
|
SMap.prototype._drop = function (id, def) {
|
|
27831
29951
|
var m = this.engine;
|
|
27832
29952
|
if (!m) { return; }
|
|
27833
29953
|
def.engineLayers.forEach(function (l) { if (m.getLayer(l)) { m.removeLayer(l); } });
|
|
27834
|
-
if (m.getSource('smap-' + id)) { m.removeSource('smap-' + id); }
|
|
29954
|
+
if (def.kind !== 'style' && m.getSource('smap-' + id)) { m.removeSource('smap-' + id); }
|
|
27835
29955
|
if (m.getSource('smap-' + id + '-hover')) { m.removeSource('smap-' + id + '-hover'); }
|
|
27836
29956
|
def.hoverLayer = null; def.hovered = null;
|
|
27837
29957
|
(this._handlers[id] || []).forEach(function (h) { if (h[1]) { m.off(h[0], h[1], h[2]); } else { m.off(h[0], h[2]); } });
|
|
@@ -27840,9 +29960,13 @@ function smapCreateSdk(engine) {
|
|
|
27840
29960
|
};
|
|
27841
29961
|
|
|
27842
29962
|
SMap.prototype._restore = function () {
|
|
27843
|
-
var self = this;
|
|
29963
|
+
var self = this, m = this.engine;
|
|
27844
29964
|
if (this.engine.setProjection) { this.engine.setProjection({ type: this._projection }); }
|
|
27845
29965
|
this._order.forEach(function (id) { self._layers[id].engineLayers = []; self._handlers[id] = []; });
|
|
29966
|
+
this._snapshotBasemap();
|
|
29967
|
+
this._applyBasemap();
|
|
29968
|
+
Object.keys(this._basemapEdits).forEach(function (id) { self._applyEdits(id, self._basemapEdits[id]); });
|
|
29969
|
+
this._sourceOrder.forEach(function (id) { if (!m.getSource(id)) { self._engineSource(id); } });
|
|
27846
29970
|
this._applyBuildings();
|
|
27847
29971
|
this._order.forEach(function (id) { self._add(id, self._layers[id]); });
|
|
27848
29972
|
};
|
|
@@ -27850,8 +29974,17 @@ function smapCreateSdk(engine) {
|
|
|
27850
29974
|
SMap.prototype._add = function (id, def) {
|
|
27851
29975
|
var m = this.engine;
|
|
27852
29976
|
var o = def.o;
|
|
27853
|
-
var src = 'smap-' + id;
|
|
27854
29977
|
var before = this._beforeId(id);
|
|
29978
|
+
if (def.kind === 'style') {
|
|
29979
|
+
// Passed to the engine as it is (a copy, so the engine never holds the object you gave).
|
|
29980
|
+
var styleLayer = clone(def.layer);
|
|
29981
|
+
if (def.hidden) { styleLayer.layout = Object.assign({}, styleLayer.layout || {}, { visibility: 'none' }); }
|
|
29982
|
+
if (typeof styleLayer.source === 'string' && this._sources[styleLayer.source] && !m.getSource(styleLayer.source)) { this._engineSource(styleLayer.source); }
|
|
29983
|
+
m.addLayer(styleLayer, before);
|
|
29984
|
+
def.engineLayers.push(styleLayer.id);
|
|
29985
|
+
return;
|
|
29986
|
+
}
|
|
29987
|
+
var src = 'smap-' + id;
|
|
27855
29988
|
var range = {};
|
|
27856
29989
|
if (o.minzoom !== undefined) { range.minzoom = o.minzoom; }
|
|
27857
29990
|
if (o.maxzoom !== undefined) { range.maxzoom = o.maxzoom; }
|
|
@@ -27978,7 +30111,9 @@ function smapCreateSdk(engine) {
|
|
|
27978
30111
|
add({
|
|
27979
30112
|
id: main, type: 'circle', filter: only,
|
|
27980
30113
|
paint: {
|
|
27981
|
-
|
|
30114
|
+
// simplestyle (marker-color, marker-size) when the layer gives no colour / size of its own
|
|
30115
|
+
'circle-color': o.color ? color : ['to-color', ['get', 'marker-color'], color],
|
|
30116
|
+
'circle-radius': o.radius || (o.color ? 6 : ['match', ['get', 'marker-size'], 'small', 4, 'large', 9, 6]),
|
|
27982
30117
|
'circle-stroke-width': o.strokeWidth !== undefined ? o.strokeWidth : 1.5, 'circle-stroke-color': o.strokeColor || '#ffffff',
|
|
27983
30118
|
},
|
|
27984
30119
|
});
|
|
@@ -27994,7 +30129,9 @@ function smapCreateSdk(engine) {
|
|
|
27994
30129
|
}
|
|
27995
30130
|
} else if (def.kind === 'lines') {
|
|
27996
30131
|
main = src + '-lines';
|
|
27997
|
-
|
|
30132
|
+
// simplestyle (stroke, stroke-width, stroke-opacity) when the layer gives no colour of its own
|
|
30133
|
+
var simple = o.color === undefined || o.color === null;
|
|
30134
|
+
var lineColor = simple ? ['to-color', ['get', 'stroke'], '#4f46e5'] : valueExpr(o.color, '#4f46e5');
|
|
27998
30135
|
var round = { 'line-cap': 'round', 'line-join': 'round' };
|
|
27999
30136
|
// Bottom to top: casing, shadow, the line, its arrows (as on Superroute's route map).
|
|
28000
30137
|
if (o.casing) {
|
|
@@ -28007,7 +30144,11 @@ function smapCreateSdk(engine) {
|
|
|
28007
30144
|
add({ id: src + '-shadow', type: 'line', layout: round, paint: {
|
|
28008
30145
|
'line-color': valueExpr(sh.color, '#000000'), 'line-width': sh.width || 7, 'line-opacity': sh.opacity !== undefined ? sh.opacity : 0.3 } });
|
|
28009
30146
|
}
|
|
28010
|
-
var paint = {
|
|
30147
|
+
var paint = {
|
|
30148
|
+
'line-color': lineColor,
|
|
30149
|
+
'line-width': o.width || (simple ? numberProp('stroke-width', 3) : 3),
|
|
30150
|
+
'line-opacity': o.opacity !== undefined ? o.opacity : (simple ? numberProp('stroke-opacity', 1) : 1),
|
|
30151
|
+
};
|
|
28011
30152
|
if (o.dash) { paint['line-dasharray'] = o.dash; }
|
|
28012
30153
|
add({ id: main, type: 'line', layout: round, paint: paint });
|
|
28013
30154
|
if (o.arrows) {
|
|
@@ -28038,8 +30179,16 @@ function smapCreateSdk(engine) {
|
|
|
28038
30179
|
});
|
|
28039
30180
|
} else {
|
|
28040
30181
|
main = src + '-fill';
|
|
28041
|
-
|
|
28042
|
-
|
|
30182
|
+
if (!o.fill && !o.color) {
|
|
30183
|
+
// simplestyle (fill, fill-opacity, stroke, stroke-width, stroke-opacity) when the layer gives no colour
|
|
30184
|
+
add({ id: main, type: 'fill', paint: { 'fill-color': ['to-color', ['get', 'fill'], color], 'fill-opacity': o.opacity !== undefined ? o.opacity : numberProp('fill-opacity', 0.25) } });
|
|
30185
|
+
add({ id: src + '-outline', type: 'line', paint: {
|
|
30186
|
+
'line-color': o.outline || ['to-color', ['get', 'stroke'], ['get', 'fill'], color],
|
|
30187
|
+
'line-width': o.outlineWidth || numberProp('stroke-width', 1.5), 'line-opacity': numberProp('stroke-opacity', 1) } });
|
|
30188
|
+
} else {
|
|
30189
|
+
add({ id: main, type: 'fill', paint: { 'fill-color': o.fill || color, 'fill-opacity': o.opacity !== undefined ? o.opacity : 0.25 } });
|
|
30190
|
+
add({ id: src + '-outline', type: 'line', paint: { 'line-color': o.outline || o.fill || color, 'line-width': o.outlineWidth || 1.5 } });
|
|
30191
|
+
}
|
|
28043
30192
|
}
|
|
28044
30193
|
}
|
|
28045
30194
|
// The popup is opened by the map's one click handler (_popupClick), from this layer's own hit test.
|
|
@@ -28110,13 +30259,28 @@ function smapCreateSdk(engine) {
|
|
|
28110
30259
|
(this._handlers[id] = this._handlers[id] || []).push([type, layer, fn]);
|
|
28111
30260
|
};
|
|
28112
30261
|
|
|
28113
|
-
return {
|
|
30262
|
+
return {
|
|
30263
|
+
Map: SMap, version: VERSION, styles: FLAVORS.slice(), engine: engine, popupTemplate: popupTemplate,
|
|
30264
|
+
slots: SLOTS.slice(),
|
|
30265
|
+
formats: interop.formats,
|
|
30266
|
+
convert: interop.convert,
|
|
30267
|
+
rasterUrl: interop.rasterUrl,
|
|
30268
|
+
rasterTileJsonUrl: interop.rasterTileJsonUrl,
|
|
30269
|
+
styleUrl: interop.styleUrl,
|
|
30270
|
+
applyBasemapOverrides: interop.applyBasemapOverrides,
|
|
30271
|
+
basemapClasses: interop.basemapClasses,
|
|
30272
|
+
slotAnchors: interop.slotAnchors,
|
|
30273
|
+
parseWmtsCapabilities: interop.parseWmtsCapabilities,
|
|
30274
|
+
PMTilesArchive: interop.PMTilesArchive,
|
|
30275
|
+
useDeck: interop.useDeck,
|
|
30276
|
+
};
|
|
28114
30277
|
});
|
|
28115
30278
|
|
|
28116
30279
|
return module.exports;
|
|
28117
30280
|
}
|
|
30281
|
+
var smapInterop = smapCreateInterop();
|
|
28118
30282
|
function smapLoad() {
|
|
28119
|
-
if (!smapCore) { smapCore = smapCreateSdk(smapCreateEngine()); }
|
|
30283
|
+
if (!smapCore) { smapCore = smapCreateSdk(smapCreateEngine(), smapInterop); }
|
|
28120
30284
|
return smapCore;
|
|
28121
30285
|
}
|
|
28122
30286
|
// Every top-level name starts with smap: the engine and the SDK run inside this scope and must keep seeing the
|
|
@@ -28130,20 +30294,45 @@ function SMapMap(options) {
|
|
|
28130
30294
|
SMapMap.prototype = core.Map.prototype;
|
|
28131
30295
|
return new core.Map(options);
|
|
28132
30296
|
}
|
|
28133
|
-
var smapVersion = "1.
|
|
30297
|
+
var smapVersion = "1.1.1";
|
|
28134
30298
|
var smapStyles = ['light', 'white', 'grayscale', 'dark', 'black'];
|
|
28135
30299
|
/** The bundled rendering engine, for anything the SDK does not wrap (loaded on the first call). */
|
|
28136
30300
|
function smapGetEngine() { return smapLoad().engine; }
|
|
28137
30301
|
// Templates need no engine: before the first map they come from an SDK made without one.
|
|
28138
30302
|
var smapNoEngine = null;
|
|
28139
30303
|
function smapPopupTemplate(template) {
|
|
28140
|
-
if (!smapCore && !smapNoEngine) { smapNoEngine = smapCreateSdk(null); }
|
|
30304
|
+
if (!smapCore && !smapNoEngine) { smapNoEngine = smapCreateSdk(null, smapInterop); }
|
|
28141
30305
|
return (smapCore || smapNoEngine).popupTemplate(template);
|
|
28142
30306
|
}
|
|
28143
|
-
var
|
|
30307
|
+
var smapSlots = ['bottom', 'roads', 'labels', 'top'];
|
|
30308
|
+
var smap_formats = smapInterop.formats;
|
|
30309
|
+
var smap_convert = smapInterop.convert;
|
|
30310
|
+
var smap_rasterUrl = smapInterop.rasterUrl;
|
|
30311
|
+
var smap_rasterTileJsonUrl = smapInterop.rasterTileJsonUrl;
|
|
30312
|
+
var smap_styleUrl = smapInterop.styleUrl;
|
|
30313
|
+
var smap_applyBasemapOverrides = smapInterop.applyBasemapOverrides;
|
|
30314
|
+
var smap_basemapClasses = smapInterop.basemapClasses;
|
|
30315
|
+
var smap_slotAnchors = smapInterop.slotAnchors;
|
|
30316
|
+
var smap_parseWmtsCapabilities = smapInterop.parseWmtsCapabilities;
|
|
30317
|
+
var smap_PMTilesArchive = smapInterop.PMTilesArchive;
|
|
30318
|
+
var smap_useDeck = smapInterop.useDeck;
|
|
30319
|
+
var smapDefault = { Map: SMapMap, version: smapVersion, styles: smapStyles, getEngine: smapGetEngine, popupTemplate: smapPopupTemplate,
|
|
30320
|
+
slots: smapSlots, formats: smap_formats, convert: smap_convert, rasterUrl: smap_rasterUrl, rasterTileJsonUrl: smap_rasterTileJsonUrl, styleUrl: smap_styleUrl, applyBasemapOverrides: smap_applyBasemapOverrides, basemapClasses: smap_basemapClasses, slotAnchors: smap_slotAnchors, parseWmtsCapabilities: smap_parseWmtsCapabilities, PMTilesArchive: smap_PMTilesArchive, useDeck: smap_useDeck };
|
|
28144
30321
|
exports.Map = SMapMap;
|
|
28145
30322
|
exports.version = smapVersion;
|
|
28146
30323
|
exports.styles = smapStyles;
|
|
28147
30324
|
exports.getEngine = smapGetEngine;
|
|
28148
30325
|
exports.popupTemplate = smapPopupTemplate;
|
|
30326
|
+
exports.slots = smapSlots;
|
|
30327
|
+
exports.formats = smap_formats;
|
|
30328
|
+
exports.convert = smap_convert;
|
|
30329
|
+
exports.rasterUrl = smap_rasterUrl;
|
|
30330
|
+
exports.rasterTileJsonUrl = smap_rasterTileJsonUrl;
|
|
30331
|
+
exports.styleUrl = smap_styleUrl;
|
|
30332
|
+
exports.applyBasemapOverrides = smap_applyBasemapOverrides;
|
|
30333
|
+
exports.basemapClasses = smap_basemapClasses;
|
|
30334
|
+
exports.slotAnchors = smap_slotAnchors;
|
|
30335
|
+
exports.parseWmtsCapabilities = smap_parseWmtsCapabilities;
|
|
30336
|
+
exports.PMTilesArchive = smap_PMTilesArchive;
|
|
30337
|
+
exports.useDeck = smap_useDeck;
|
|
28149
30338
|
exports.default = smapDefault;
|