@nextgis/ngw-map
Version:
686 lines (678 loc) • 20.4 kB
JavaScript
/** Bundle of @nextgis/ngw-map; version: 3.0.1; author: NextGIS */
;
Object.defineProperty(exports, '__esModule', { value: true });
var icons = require('@nextgis/icons');
var NgwConnector = require('@nextgis/ngw-connector');
var ngwKit = require('@nextgis/ngw-kit');
var utils = require('@nextgis/utils');
var webmap = require('@nextgis/webmap');
var events = require('events');
var qmsKit = require('@nextgis/qms-kit');
function appendNgwResources(options, resource, defOptions, overwriteOptions) {
if (typeof resource === "number" || typeof resource === "string") {
resource = Number(resource);
options.push({
...defOptions,
resource
});
} else if (Array.isArray(resource)) {
const [resourceId, id] = resource;
options.push({
...defOptions,
resource: resourceId,
id,
...overwriteOptions
});
} else if (typeof resource === "object") {
options.push({ ...defOptions, ...resource, ...overwriteOptions });
}
}
const OPTIONS = {
target: "map",
baseUrl: "",
whitlabel: false,
controls: webmap.getDefaultControls(),
controlsOptions: {
ZOOM: { position: "top-left" },
ATTRIBUTION: {
position: "bottom-right",
customAttribution: [
'<a href="https://nextgis.com" target="_blank">\xA9NextGIS</a>'
]
}
},
pixelRadius: 10
};
function prepareWebMapOptions(options) {
const kits = [new qmsKit.QmsKit()];
if (options.starterKits) {
options.starterKits.forEach((x) => {
kits.push(x);
});
}
if (!options.connector) {
options.connector = new NgwConnector({
baseUrl: options.baseUrl || "",
auth: options.auth,
withCredentials: options.withCredentials
});
} else if (options.connector) {
options.baseUrl = options.connector.options.baseUrl;
}
options = utils.deepmerge(OPTIONS, options);
if (!options.center && !options.bounds) {
options.bounds = [-179, -90, 180, 90];
}
if (options.connector) {
kits.push(
new ngwKit.NgwKit({
connector: options.connector,
auth: options.auth
})
);
}
options = {
...options,
starterKits: kits,
create: false
};
return options;
}
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
class NgwMap extends webmap.WebMap {
constructor(options) {
super(prepareWebMapOptions(options));
__publicField(this, "emitter", new events.EventEmitter());
__publicField(this, "connector");
__publicField(this, "_ngwLayers", {});
__publicField(this, "$$selectFromNgwRaster");
__publicField(this, "$$selectFromNgwVector");
__publicField(this, "_promises", {
select: [],
identify: []
});
if (options.connector) {
this.connector = options.connector;
}
this._createWebMap().then(() => {
const container = this.getContainer();
if (container) {
container.classList.add("ngw-map-container");
}
if (this.options.whitlabel) {
this._whiteLabel();
}
});
}
/**
* Organized addition to the map design and controls elements,
* calling `control.onAdd(this.webMap.mapAdapter)`
* @param control - object with onAdd and onRemove methods
* or a string value indicating the name of the control installed in the map adapter
* @param position - position relative to the map angles
* @param options - initialization parameters if the control is set as a string value
*
* @example
* ```javascript
* ngwMap.addControl(new CustomControl(), 'bottom-left');
* ngwMap.addControl('ZOOM', 'top-right')
* ```
*/
async addControl(controlDef, position, options) {
await this.onLoad("controls:create");
return super.addControl(controlDef, position, options);
}
/**
* Add any (style, vector, webmap) NGW layer by resource definition.
* @param options - set layer identification parameters and render method.
*
* @example
* ```javascript
* // Add raster layer resourceId is the style of 4004 layer
* ngwMap.addNgwLayer({ resource: 4005 });
* // Add vector data from layer GEOJSON source
* ngwMap.addNgwLayer({
* resource: 4038,
* adapter: 'GEOJSON',
* adapterOptions: { paint: { color: 'red' } }
* });
* ```
*/
async addNgwLayer(options) {
await this.onMapLoad();
const { keyname, resourceId } = options;
if (keyname || resourceId !== void 0) {
utils.deprecatedWarn(
"set `resource` options instead of `keyname` or `resourceId`"
);
}
const resource = options.resource;
if (!keyname && !resourceId && !resource) {
throw new Error(
"resource, resourceId or keyname is required parameter to add NGW layer"
);
}
if (utils.defined(this.options.baseUrl)) {
try {
if (utils.defined(this.options.setViewDelay)) {
options.adapterOptions = options.adapterOptions || {};
if (!utils.defined(options.adapterOptions.setViewDelay)) {
options.adapterOptions.setViewDelay = this.options.setViewDelay;
}
}
const adapter = ngwKit.createNgwLayerAdapter(options, this, this.connector);
const adapterOpts = {
visibility: true,
// TODO: do not merge options, use only `adapterOptions`
...options,
...options.adapterOptions
};
const layer = await this.addLayer(
adapter,
adapterOpts
);
const id = layer && this.getLayerId(layer);
if (layer && id) {
this._ngwLayers[id] = { layer, resourceId: layer.resourceId };
layer.options.name = layer.options.name || layer.item && layer.item.resource.display_name;
if (layer.options.baselayer) {
const visibleLayerBaseLayer = this.getActiveBaseLayer();
if (visibleLayerBaseLayer) {
return layer;
}
}
}
return layer;
} catch (er) {
const resId = utils.isObject(resource) && "id" in resource ? resource.id : keyname || resourceId || resource;
console.error(`Can't add NGW layer ${resId}.`, er);
}
}
}
/**
* Pans and zooms the map to the initial position specified in the options
*/
fit() {
const { center, zoom, bounds } = this.options;
if (center) {
this.setCenter(center);
if (zoom) {
this.setZoom(zoom);
}
} else if (bounds) {
this.fitBounds(bounds);
}
}
fetchNgwLayerItem(options) {
return ngwKit.fetchNgwLayerItem({
connector: this.connector,
...options
});
}
fetchNgwLayerItems(options) {
return ngwKit.fetchNgwLayerItems({
connector: this.connector,
...options
});
}
fetchNgwLayerFeature(options) {
return ngwKit.fetchNgwLayerFeature({
connector: this.connector,
...options
});
}
fetchNgwLayerFeatures(options) {
return ngwKit.fetchNgwLayerFeatureCollection({
connector: this.connector,
...options
});
}
fetchIdentifyItem(identify, requestOptions) {
const abortController = new AbortController();
const abortSignal = abortController.signal;
if (requestOptions == null ? void 0 : requestOptions.signal) {
requestOptions.signal.addEventListener("abort", abortController.abort);
}
requestOptions = requestOptions || {};
requestOptions.signal = abortSignal;
const promise = ngwKit.fetchIdentifyItem({
identify,
connector: this.connector,
requestOptions
// multiple,
});
this._addPromise("identify", promise, abortController);
return promise;
}
fetchIdentifyGeoJson(identify, { multiple, signal } = {}) {
const abortController = new AbortController();
if (signal) {
if (signal.aborted) {
return Promise.reject(new NgwConnector.errors.AbortError());
}
signal.addEventListener("abort", () => {
abortController.abort("AbortError");
});
}
const promise = ngwKit.fetchIdentifyGeoJson({
identify,
connector: this.connector,
multiple,
requestOptions: { signal: abortController.signal }
});
if (promise && "then" in promise) {
this._addPromise("identify", promise, abortController);
return promise;
} else {
return Promise.resolve(promise);
}
}
/**
* @deprecated use {@link fetchIdentifyGeoJson} instead
*/
getIdentifyGeoJson(identify, multiple = false) {
return this.fetchIdentifyGeoJson(identify, { multiple });
}
async getNgwLayers() {
await this.onLoad();
return this._ngwLayers;
}
async getNgwLayerByResourceId(id) {
for (const n in this._ngwLayers) {
const mem = this._ngwLayers[n];
if (mem.resourceId === id) {
return mem && mem.layer;
} else if (mem.layer.getIdentificationIds) {
const ids = await mem.layer.getIdentificationIds();
if (ids && ids.some((x) => x === id)) {
return mem.layer;
}
}
if (mem.layer.getDependLayers) {
const dependLayers = mem.layer.getDependLayers();
const dependFit = dependLayers.find((x) => {
return x.item && "style_parent_id" in x.item && x.item.style_parent_id !== void 0 && x.item.style_parent_id === id;
});
if (dependFit) {
return dependFit.layer;
}
}
}
}
/**
* Move map to layer. If the layer is NGW resource, extent will be received from the server
*
* @example
* ```javascript
* const ngwLayer = ngwMap.addNgwLayer({ id: 'ngw_layer_name', resource: 4005 });
* ngwMap.fitLayer(ngwLayer);
* ngwMap.fitLayer('ngw_layer_name');
* ```
*/
async fitLayer(layerDef, options) {
let id;
if (typeof layerDef === "string" || typeof layerDef === "number") {
id = String(id);
} else {
id = layerDef.id;
}
const ngwLayer = id && this._ngwLayers[id];
if (ngwLayer) {
if (ngwLayer.layer.getBounds) {
const bounds = await ngwLayer.layer.getBounds();
if (bounds) {
this.fitBounds(bounds, options);
}
} else {
let item;
if (ngwLayer.layer.item) {
item = ngwLayer.layer.item;
} else {
const resourceId = ngwLayer.resourceId;
item = await this.connector.getResource(resourceId);
}
if (item) {
this.fitResource(item.resource.id);
}
}
} else {
super.fitLayer(
typeof layerDef === "number" ? String(layerDef) : layerDef,
options
);
}
}
async fitResource(resource, options) {
const resourceId = await this.connector.resources.getIdOrFail(resource);
const extent = await ngwKit.fetchNgwExtent({
resourceId,
connector: this.connector
});
if (extent) {
this.fitBounds(extent, options);
}
}
/** @deprecated use {@link fitLayer} instead */
async zoomToLayer(layerDef) {
return this.fitLayer(layerDef);
}
onLoad(event = "ngw-map:create") {
return super.onLoad(event);
}
removeLayer(layerDef) {
const layer = this.getLayer(layerDef);
if (layer) {
const layerId = this.getLayerId(layer);
if (layerId) {
delete this._ngwLayers[layerId];
}
super.removeLayer(layer);
}
}
enableSelection() {
if (!this.$$selectFromNgwRaster) {
this.$$selectFromNgwRaster = (ev) => {
const count = this._getSelectListenersCount();
if (count) {
this.selectFromNgwRaster(ev);
}
};
this.$$selectFromNgwVector = (ev) => {
const count = this._getSelectListenersCount();
if (count) {
this._selectFromNgwVector(ev);
}
};
this.emitter.on("click", this.$$selectFromNgwRaster);
this.emitter.on("layer:click", this.$$selectFromNgwVector);
}
}
disableSelection() {
if (this.$$selectFromNgwRaster) {
this.emitter.removeListener("click", this.$$selectFromNgwRaster);
this.$$selectFromNgwRaster = void 0;
}
if (this.$$selectFromNgwVector) {
this.emitter.removeListener("layer:click", this.$$selectFromNgwVector);
this.$$selectFromNgwVector = void 0;
}
}
/**
* @deprecated use {@link fetchNgwLayerItem} instead
*/
getNgwLayerItem(options) {
return this.fetchNgwLayerItem(options);
}
/**
* @deprecated use {@link fetchNgwLayerItems} instead
*/
getNgwLayerItems(options) {
return this.fetchNgwLayerItems(options);
}
/**
* @deprecated use {@link fetchNgwLayerFeature} instead
*/
getNgwLayerFeature(options) {
return this.fetchNgwLayerFeature(options);
}
/**
* @deprecated use {@link fetchNgwLayerFeatures} instead
*/
getNgwLayerFeatures(options) {
return this.fetchNgwLayerFeatures(options);
}
/** @deprecated use {@link cancelPromises} instead */
cancelPromise(...args) {
this.cancelPromises(...args);
}
cancelPromises(...args) {
if (!args.length) {
args = Object.keys(this._promises);
}
args.forEach((name) => {
const group = this._promises[name];
if (group) {
group.forEach((x) => x[1].abort());
this._promises[name] = [];
}
});
}
async selectFromNgwRaster(ev) {
var _a;
this._emitStatusEvent("ngw:preselect");
const promises = [];
const layers = Object.values(this._ngwLayers);
layers.sort((a, b) => {
if (a.layer.order && b.layer.order) {
return b.layer.order - a.layer.order;
}
return 1;
});
for (const l of layers) {
const layer = l.layer;
const identFunc = typeof layer.getIdentificationIds === "function" ? layer.getIdentificationIds : false;
const interactive = (_a = layer.options.interactive) != null ? _a : true;
if (identFunc && layer.options.selectable && interactive && this.isLayerVisible(layer)) {
const layerIds = identFunc.call(layer);
promises.push(layerIds);
}
}
const getIdsPromise = Promise.all(promises);
const getIds = await getIdsPromise;
const ids = [];
for (const x of getIds) {
if (x) {
ids.push(...x);
}
}
if (!ids.length) {
this._emitStatusEvent("ngw:select", null);
return;
}
const pixelRadius = this.options.pixelRadius || 10;
const center = this.getCenter();
let zoom = this.getZoom();
zoom = zoom !== void 0 ? zoom : 20;
if (!center || !zoom) {
this._emitStatusEvent("ngw:select", null);
return;
}
const radius = utils.getIdentifyRadius(center, zoom, pixelRadius);
let geom;
if (this.options.highlightIdentification) {
const highlightOptions = this.options.highlightIdentification;
const highlightDuration = typeof highlightOptions === "number" ? highlightOptions : 1e3;
const [lng, lat] = ev.lngLat;
geom = {
type: "Polygon",
coordinates: [utils.getCirclePolygonCoordinates(lng, lat, radius)]
};
const highlightIdentificationLayer = await this.addGeoJsonLayer({
data: {
type: "Feature",
geometry: geom
}
});
if (highlightDuration !== Infinity) {
setTimeout(() => {
this.removeLayer(highlightIdentificationLayer);
}, highlightDuration);
}
}
const abortController = new AbortController();
const selectPromise = ngwKit.sendIdentifyRequest(ev, {
layers: ids,
connector: this.connector,
radius,
geom,
signal: abortController.signal
}).then((resp) => {
const identify = {
...resp,
resources: ids,
sourceType: "raster",
event: ev
};
const identifyEvent = this._prepareToIdentify(identify);
this._emitStatusEvent("ngw:select", identifyEvent);
return identifyEvent;
});
this._addPromise("select", selectPromise, abortController);
return selectPromise;
}
_addPromise(groupName, promise, abortController) {
const group = this._promises[groupName];
if (group && group.findIndex((g) => g[0] === promise) === -1) {
const removeFromGroup = () => {
const index = group.findIndex((g) => g[0] === promise);
if (index !== -1) {
group.splice(index, 1);
}
};
promise.then(removeFromGroup, removeFromGroup);
group.push([promise, abortController]);
}
}
_isFitFromResource() {
const params = this._initMapState;
if (params.zoom && params.center) {
return false;
}
return true;
}
async _createWebMap() {
await this.create();
if (this.options.qmsId) {
this._addQmsBaseLayer();
}
if (this.options.osm) {
this._addOsmBaseLayer();
}
const resources = [];
const layerFitAllowed = this._isFitFromResource();
if (this.options.webmapId) {
appendNgwResources(resources, this.options.webmapId, {
fit: layerFitAllowed
});
}
if (this.options.resources && Array.isArray(this.options.resources)) {
for (const x of this.options.resources) {
const overwriteOptions = {};
if (!layerFitAllowed) {
overwriteOptions.fit = false;
}
appendNgwResources(resources, x, {}, overwriteOptions);
}
}
for (const r of resources) {
try {
await this.addNgwLayer(r);
} catch (er) {
console.warn(er);
}
}
this._emitStatusEvent("ngw-map:create", this);
this.enableSelection();
}
_addOsmBaseLayer() {
this.addBaseLayer("OSM");
}
_addQmsBaseLayer() {
let qmsId;
let qmsLayerName;
if (Array.isArray(this.options.qmsId)) {
qmsId = this.options.qmsId[0];
qmsLayerName = this.options.qmsId[1];
} else {
qmsId = Number(this.options.qmsId);
}
const qmsLayerOptions = {
qmsId
};
if (qmsLayerName) {
qmsLayerOptions.id = qmsLayerName;
}
this.addBaseLayer("QMS", qmsLayerOptions);
}
_selectFromNgwVector(ev) {
const layer = ev.layer;
const selectable = layer.options.selectable && this.isLayerVisible(layer);
if (!selectable) {
return void 0;
}
const id = layer.item && layer.item.resource.id;
const feature = ev.feature;
if (id !== void 0 && feature) {
const featureId = feature.id;
if (featureId) {
const identifyFeature = {
id: Number(featureId),
fields: feature.properties || {},
label: `#${id}`,
layerId: Number(id),
parent: "",
geom: feature.geometry
};
const items = {
featureCount: 1,
features: [identifyFeature]
};
const identify = {
featureCount: 1,
[id]: items
};
this._emitStatusEvent(
"ngw:select",
this._prepareToIdentify({
...identify,
resources: [id],
sourceType: "vector"
})
);
return identify;
}
}
}
_prepareToIdentify(identify) {
const getIdentifyItems_ = () => {
return ngwKit.getIdentifyItems(identify, true).map((x) => {
return ngwKit.createIdentifyItem({
feature: x.feature,
connector: this.connector
});
});
};
return {
...identify,
getIdentifyItems: getIdentifyItems_
};
}
_getSelectListenersCount() {
return this.emitter.listenerCount("ngw:select");
}
async _whiteLabel() {
const container = this.getContainer();
if (container) {
const logo = await ngwKit.getCompanyLogo(
this.connector,
this.options.companyLogoOptions
);
if (logo) {
container.appendChild(logo);
}
}
}
}
__publicField(NgwMap, "getIcon", icons.getIcon);
async function createNgwMap(options) {
const ngwMap = new NgwMap(options);
return ngwMap.onLoad();
}
exports.NgwMap = NgwMap;
exports.createNgwMap = createNgwMap;
Object.keys(webmap).forEach(function (k) {
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = webmap[k];
});
//# sourceMappingURL=ngw-map.cjs.js.map