UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

404 lines (403 loc) • 15 kB
/** * DevExtreme (esm/__internal/ui/map/m_provider.dynamic.bing.js) * Version: 24.2.6 * Build date: Mon Mar 17 2025 * * Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import Color from "../../../color"; import ajax from "../../../core/utils/ajax"; import { noop } from "../../../core/utils/common"; import { extend } from "../../../core/utils/extend"; import { each, map } from "../../../core/utils/iterator"; import { getHeight, getWidth } from "../../../core/utils/size"; import { isDefined } from "../../../core/utils/type"; import { getWindow } from "../../../core/utils/window"; import errors from "../../../ui/widget/ui.errors"; import DynamicProvider from "./m_provider.dynamic"; const window = getWindow(); const BING_MAP_READY = "_bingScriptReady"; let BING_URL_V8 = `https://www.bing.com/api/maps/mapcontrol?callback=${BING_MAP_READY}`; const INFOBOX_V_OFFSET_V8 = 13; const MIN_LOCATION_RECT_LENGTH = 1e-16; const msMapsLoaded = function() { return window.Microsoft && window.Microsoft.Maps }; let msMapsLoader; class BingProvider extends DynamicProvider { _mapType(type) { const mapTypes = { roadmap: Microsoft.Maps.MapTypeId.road, hybrid: Microsoft.Maps.MapTypeId.aerial, satellite: Microsoft.Maps.MapTypeId.aerial }; return mapTypes[type] || mapTypes.road } _movementMode(type) { const movementTypes = { driving: Microsoft.Maps.Directions.RouteMode.driving, walking: Microsoft.Maps.Directions.RouteMode.walking }; return movementTypes[type] || movementTypes.driving } _resolveLocation(location) { return new Promise((resolve => { const latLng = this._getLatLng(location); if (latLng) { resolve(new Microsoft.Maps.Location(latLng.lat, latLng.lng)) } else { this._geocodeLocation(location).then((geocodedLocation => { resolve(geocodedLocation) })) } })) } _geocodeLocationImpl(location) { return new Promise((resolve => { if (!isDefined(location)) { resolve(new Microsoft.Maps.Location(0, 0)); return } const searchManager = new Microsoft.Maps.Search.SearchManager(this._map); const searchRequest = { where: location, count: 1, callback(searchResponse) { const result = searchResponse.results[0]; if (result) { const boundsBox = searchResponse.results[0].location; resolve(new Microsoft.Maps.Location(boundsBox.latitude, boundsBox.longitude)) } else { resolve(new Microsoft.Maps.Location(0, 0)) } } }; searchManager.geocode(searchRequest) })) } _normalizeLocation(location) { return { lat: location.latitude, lng: location.longitude } } _normalizeLocationRect(locationRect) { const northWest = this._normalizeLocation(locationRect.getNorthwest()); const southEast = this._normalizeLocation(locationRect.getSoutheast()); return { northEast: { lat: northWest.lat, lng: southEast.lng }, southWest: { lat: southEast.lat, lng: northWest.lng } } } _loadImpl() { return new Promise((resolve => { if (msMapsLoaded()) { resolve() } else { if (!msMapsLoader) { msMapsLoader = this._loadMapScript() } msMapsLoader.then((() => { if (msMapsLoaded()) { resolve(); return } this._loadMapScript().then(resolve) })) } })).then((() => Promise.all([new Promise((resolve => { Microsoft.Maps.loadModule("Microsoft.Maps.Search", { callback: resolve }) })), new Promise((resolve => { Microsoft.Maps.loadModule("Microsoft.Maps.Directions", { callback: resolve }) }))]))) } _loadMapScript() { return new Promise((resolve => { window[BING_MAP_READY] = resolve; ajax.sendRequest({ url: BING_URL_V8, dataType: "script" }) })).then((() => { try { delete window[BING_MAP_READY] } catch (e) { window[BING_MAP_READY] = void 0 } })) } _init() { this._createMap(); return Promise.resolve() } _createMap() { const controls = this._option("controls"); this._map = new Microsoft.Maps.Map(this._$container[0], { credentials: this._keyOption("bing"), zoom: this._option("zoom"), showDashboard: controls, showMapTypeSelector: controls, showScalebar: controls }) } _attachHandlers() { this._providerViewChangeHandler = Microsoft.Maps.Events.addHandler(this._map, "viewchange", this._viewChangeHandler.bind(this)); this._providerClickHandler = Microsoft.Maps.Events.addHandler(this._map, "click", this._clickActionHandler.bind(this)) } _viewChangeHandler() { const bounds = this._map.getBounds(); this._option("bounds", this._normalizeLocationRect(bounds)); const center = this._map.getCenter(); this._option("center", this._normalizeLocation(center)); if (!this._preventZoomChangeEvent) { this._option("zoom", this._map.getZoom()) } } _clickActionHandler(e) { if ("map" === e.targetType) { this._fireClickAction({ location: this._normalizeLocation(e.location) }) } } updateDimensions() { const $container = this._$container; this._map.setOptions({ width: getWidth($container), height: getHeight($container) }); return Promise.resolve() } updateMapType() { const type = this._option("type"); const labelOverlay = Microsoft.Maps.LabelOverlay; this._map.setView({ animate: false, mapTypeId: this._mapType(type), labelOverlay: "satellite" === type ? labelOverlay.hidden : labelOverlay.visible }); return Promise.resolve() } updateBounds() { return Promise.all([this._resolveLocation(this._option("bounds.northEast")), this._resolveLocation(this._option("bounds.southWest"))]).then((result => { const bounds = new Microsoft.Maps.LocationRect.fromLocations(result[0], result[1]); this._map.setView({ animate: false, bounds: bounds }) })) } updateCenter() { return this._resolveLocation(this._option("center")).then((center => { this._map.setView({ animate: false, center: center }) })) } updateZoom() { this._map.setView({ animate: false, zoom: this._option("zoom") }); return Promise.resolve() } updateControls() { this.clean(); return this.render.apply(this, arguments) } _renderMarker(options) { return this._resolveLocation(options.location).then((location => { const pushpinOptions = { icon: options.iconSrc || this._option("markerIconSrc") }; if (options.html) { extend(pushpinOptions, { htmlContent: options.html, width: null, height: null }); const { htmlOffset: htmlOffset } = options; if (htmlOffset) { pushpinOptions.anchor = new Microsoft.Maps.Point(-htmlOffset.left, -htmlOffset.top) } } const pushpin = new Microsoft.Maps.Pushpin(location, pushpinOptions); this._map.entities.push(pushpin); const infobox = this._renderTooltip(location, options.tooltip); let handler; if (options.onClick || options.tooltip) { const markerClickAction = this._mapWidget._createAction(options.onClick || noop); const markerNormalizedLocation = this._normalizeLocation(location); handler = Microsoft.Maps.Events.addHandler(pushpin, "click", (() => { markerClickAction({ location: markerNormalizedLocation }); if (infobox) { infobox.setOptions({ visible: true }) } })) } return { location: location, marker: pushpin, infobox: infobox, handler: handler } })) } _renderTooltip(location, options) { if (!options) { return } options = this._parseTooltipOptions(options); const infobox = new Microsoft.Maps.Infobox(location, { description: options.text, offset: new Microsoft.Maps.Point(0, 13), visible: options.visible }); infobox.setMap(this._map); return infobox } _destroyMarker(marker) { this._map.entities.remove(marker.marker); if (marker.infobox) { marker.infobox.setMap(null) } if (marker.handler) { Microsoft.Maps.Events.removeHandler(marker.handler) } } _renderRoute(options) { return Promise.all(map(options.locations, (point => this._resolveLocation(point)))).then((locations => new Promise((resolve => { const direction = new Microsoft.Maps.Directions.DirectionsManager(this._map); const color = new Color(options.color || this._defaultRouteColor()).toHex(); const routeColor = new Microsoft.Maps.Color.fromHex(color); routeColor.a = 255 * (options.opacity || this._defaultRouteOpacity()); direction.setRenderOptions({ autoUpdateMapView: false, displayRouteSelector: false, waypointPushpinOptions: { visible: false }, drivingPolylineOptions: { strokeColor: routeColor, strokeThickness: options.weight || this._defaultRouteWeight() }, walkingPolylineOptions: { strokeColor: routeColor, strokeThickness: options.weight || this._defaultRouteWeight() } }); direction.setRequestOptions({ routeMode: this._movementMode(options.mode), routeDraggable: false }); each(locations, ((_, location) => { const waypoint = new Microsoft.Maps.Directions.Waypoint({ location: location }); direction.addWaypoint(waypoint) })); const directionHandlers = []; directionHandlers.push(Microsoft.Maps.Events.addHandler(direction, "directionsUpdated", (args => { while (directionHandlers.length) { Microsoft.Maps.Events.removeHandler(directionHandlers.pop()) } const routeSummary = args.routeSummary[0]; resolve({ instance: direction, northEast: routeSummary.northEast, southWest: routeSummary.southWest }) }))); directionHandlers.push(Microsoft.Maps.Events.addHandler(direction, "directionsError", (args => { while (directionHandlers.length) { Microsoft.Maps.Events.removeHandler(directionHandlers.pop()) } const status = `RouteResponseCode: ${args.responseCode} - ${args.message}`; errors.log("W1006", status); resolve({ instance: direction }) }))); direction.calculateDirections() })))) } _destroyRoute(routeObject) { routeObject.instance.dispose() } _fitBounds() { this._updateBounds(); if (this._bounds && this._option("autoAdjust")) { const zoomBeforeFitting = this._map.getZoom(); this._preventZoomChangeEvent = true; const bounds = this._bounds.clone(); bounds.height *= 1.1; bounds.width *= 1.1; this._map.setView({ animate: false, bounds: bounds, zoom: zoomBeforeFitting }); const zoomAfterFitting = this._map.getZoom(); if (zoomBeforeFitting < zoomAfterFitting) { this._map.setView({ animate: false, zoom: zoomBeforeFitting }) } else { this._option("zoom", zoomAfterFitting) } delete this._preventZoomChangeEvent } return Promise.resolve() } _extendBounds(location) { if (this._bounds) { this._bounds = new Microsoft.Maps.LocationRect.fromLocations(this._bounds.getNorthwest(), this._bounds.getSoutheast(), location) } else { this._bounds = new Microsoft.Maps.LocationRect(location, 1e-16, 1e-16) } } clean() { if (this._map) { Microsoft.Maps.Events.removeHandler(this._providerViewChangeHandler); Microsoft.Maps.Events.removeHandler(this._providerClickHandler); this._clearMarkers(); this._clearRoutes(); this._map.dispose() } return Promise.resolve() } } export default BingProvider;