UNPKG

@progress/kendo-charts

Version:

Kendo UI platform-independent Charts library

982 lines (776 loc) 27.9 kB
import { geometry as g, throttle } from '@progress/kendo-drawing'; import { addClass, setDefaultOptions, valueOrDefault, defined, Observable, mousewheelDelta, limitValue, deepExtend, elementOffset, isArray, round, now, on, off, getSupportedFeatures, } from '../common'; import { EPSG3857 } from './crs'; import { Attribution } from './attribution'; import { Navigator } from './navigator'; import { ZoomControl } from './zoom'; import { Location } from './location'; import { Extent } from './extent'; import { Tooltip } from './tooltip/tooltip'; import { TileLayer } from './layers/tile'; import { BubbleLayer } from './layers/bubble'; import { ShapeLayer } from './layers/shape'; import { MarkerLayer } from './layers/marker'; import { removeChildren, proxy, setDefaultEvents, convertToHtml, renderPos } from './utils'; import { Scroller } from './scroller/scroller'; import MapService from './../services/map-service'; import { CENTER_CHANGE, INIT, ZOOM_CHANGE } from './constants'; var math = Math, min = math.min, pow = math.pow, Point = g.Point, MARKER = "marker", LOCATION = "location", FRICTION = 0.9, FRICTION_MOBILE = 0.93, MOUSEWHEEL = 'wheel', MOUSEWHEEL_THROTTLE = 50, VELOCITY_MULTIPLIER = 5, DEFAULT_ZOOM_RATE = 1; var layersMap = { bubble: BubbleLayer, shape: ShapeLayer, tile: TileLayer }; layersMap[MARKER] = MarkerLayer; var Map = (function (Observable) { function Map(element, options, themeOptions, context) { if ( options === void 0 ) options = {}; if ( themeOptions === void 0 ) themeOptions = {}; if ( context === void 0 ) context = {}; Observable.call(this); this._init(element, options, themeOptions, context); } if ( Observable ) Map.__proto__ = Observable; Map.prototype = Object.create( Observable && Observable.prototype ); Map.prototype.constructor = Map; Map.prototype.destroy = function destroy () { var this$1 = this; this.scroller.destroy(); if (this._tooltip) { this._tooltip.destroy(); } if (this.navigator) { this.navigator.destroy(); } if (this.attribution) { this.attribution.destroy(); } if (this.zoomControl) { this.zoomControl.destroy(); } if (isArray(this.markers)) { this.markers.forEach(function (markerLayer) { markerLayer.destroy(); }); } else { this.markers.destroy(); } for (var i = 0; i < this.layers.length; i++) { this$1.layers[i].destroy(); } off(this.element, MOUSEWHEEL, this._mousewheelHandler); Observable.prototype.destroy.call(this); }; // eslint-disable-next-line no-unused-vars Map.prototype._init = function _init (element, options, themeOptions, context) { if ( options === void 0 ) options = {}; if ( themeOptions === void 0 ) themeOptions = {}; if ( context === void 0 ) context = {}; this.support = getSupportedFeatures(); this.context = context; this.initObserver(context); this.initServices(context); this._notifyObserver(INIT); this._initOptions(options); this._setEvents(options); this.crs = new EPSG3857(); this._initElement(element); this._viewOrigin = this._getOrigin(); this._tooltip = this._createTooltip(); this._initScroller(); this._initMarkers(); this._initControls(); this._initLayers(); this._reset(); var mousewheelThrottled = throttle(this._mousewheel.bind(this), MOUSEWHEEL_THROTTLE); this._mousewheelHandler = function (e) { e.preventDefault(); mousewheelThrottled(e); }; on(this.element, MOUSEWHEEL, this._mousewheelHandler); }; Map.prototype._initOptions = function _initOptions (options) { this.options = deepExtend({}, this.options, options); }; Map.prototype._initElement = function _initElement (element) { this.element = element; addClass(element, "k-map"); element.style.position = "relative"; element.setAttribute("data-role", "map"); removeChildren(element); var div = convertToHtml("<div />"); this.element.appendChild(div); }; Map.prototype.initServices = function initServices (context) { if ( context === void 0 ) context = {}; this.widgetService = new MapService(this, context); }; Map.prototype.initObserver = function initObserver (context) { if ( context === void 0 ) context = {}; this.observers = []; this.addObserver(context.observer); }; Map.prototype.addObserver = function addObserver (observer) { if (observer) { this.observers.push(observer); } }; Map.prototype.removeObserver = function removeObserver (observer) { var index = this.observers.indexOf(observer); if (index >= 0) { this.observers.splice(index, 1); } }; Map.prototype.requiresHandlers = function requiresHandlers (eventNames) { var observers = this.observers; for (var idx = 0; idx < observers.length; idx++) { if (observers[idx].requiresHandlers(eventNames)) { return true; } } }; Map.prototype.trigger = function trigger (name, args) { if ( args === void 0 ) args = {}; args.sender = this; var observers = this.observers; var isDefaultPrevented = false; for (var idx = 0; idx < observers.length; idx++) { if (observers[idx].trigger(name, args)) { isDefaultPrevented = true; } } if (!isDefaultPrevented) { Observable.prototype.trigger.call(this, name, args); } return isDefaultPrevented; }; Map.prototype._notifyObserver = function _notifyObserver (name, args) { if ( args === void 0 ) args = {}; args.sender = this; var observers = this.observers; var isDefaultPrevented = false; for (var idx = 0; idx < observers.length; idx++) { if (observers[idx].trigger(name, args)) { isDefaultPrevented = true; } } return isDefaultPrevented; }; Map.prototype.zoom = function zoom (level) { var options = this.options; var result; if (defined(level)) { var zoomLevel = math.round(limitValue(level, options.minZoom, options.maxZoom)); if (options.zoom !== zoomLevel) { options.zoom = zoomLevel; this.widgetService.notify(ZOOM_CHANGE, { zoom: options.zoom }); this._reset(); } result = this; } else { result = options.zoom; } return result; }; Map.prototype.center = function center (center$1) { var result; if (center$1) { var current = Location.create(center$1); var previous = Location.create(this.options.center); if (!current.equals(previous)) { this.options.center = current.toArray(); this.widgetService.notify(CENTER_CHANGE, { center: this.options.center }); this._reset(); } result = this; } else { result = Location.create(this.options.center); } return result; }; Map.prototype.extent = function extent (extent$1) { var result; if (extent$1) { this._setExtent(extent$1); result = this; } else { result = this._getExtent(); } return result; }; Map.prototype.setOptions = function setOptions (options) { if ( options === void 0 ) options = {}; var element = this.element; this.destroy(); removeChildren(element); this._init(element, options, {}, this.context); this._reset(); }; Map.prototype.locationToLayer = function locationToLayer (location, zoom) { var clamp = !this.options.wraparound; var locationObject = Location.create(location); return this.crs.toPoint(locationObject, this._layerSize(zoom), clamp); }; Map.prototype.layerToLocation = function layerToLocation (point, zoom) { var clamp = !this.options.wraparound; var pointObject = Point.create(point); return this.crs.toLocation(pointObject, this._layerSize(zoom), clamp); }; Map.prototype.locationToView = function locationToView (location) { var locationObject = Location.create(location); var origin = this.locationToLayer(this._viewOrigin); var point = this.locationToLayer(locationObject); return point.translateWith(origin.scale(-1)); }; Map.prototype.viewToLocation = function viewToLocation (point, zoom) { var origin = this.locationToLayer(this._getOrigin(), zoom); var pointObject = Point.create(point); var pointResult = pointObject.clone().translateWith(origin); return this.layerToLocation(pointResult, zoom); }; Map.prototype.eventOffset = function eventOffset (e) { var x; var y; var offset = elementOffset(this.element); if ((e.x && e.x[LOCATION]) || (e.y && e.y[LOCATION])) { x = e.x[LOCATION] - offset.left; y = e.y[LOCATION] - offset.top; } else { var event = e.originalEvent || e; x = valueOrDefault(event.pageX, event.clientX) - offset.left; y = valueOrDefault(event.pageY, event.clientY) - offset.top; } var point = new g.Point(x, y); return point; }; Map.prototype.eventToView = function eventToView (e) { var cursor = this.eventOffset(e); return this.locationToView(this.viewToLocation(cursor)); }; Map.prototype.eventToLayer = function eventToLayer (e) { return this.locationToLayer(this.eventToLocation(e)); }; Map.prototype.eventToLocation = function eventToLocation (e) { var cursor = this.eventOffset(e); return this.viewToLocation(cursor); }; Map.prototype.viewSize = function viewSize () { var element = this.element; var scale = this._layerSize(); var width = element.clientWidth; if (!this.options.wraparound) { width = min(scale, width); } return { width: width, height: min(scale, element.clientHeight) }; }; Map.prototype.exportVisual = function exportVisual () { this._reset(); return false; }; Map.prototype.hideTooltip = function hideTooltip () { if (this._tooltip) { this._tooltip.hide(); } }; Map.prototype._setOrigin = function _setOrigin (origin, zoom) { var size = this.viewSize(), topLeft; var originLocation = this._origin = Location.create(origin); topLeft = this.locationToLayer(originLocation, zoom); topLeft.x += size.width / 2; topLeft.y += size.height / 2; this.options.center = this.layerToLocation(topLeft, zoom).toArray(); this.widgetService.notify(CENTER_CHANGE, { center: this.options.center }); return this; }; Map.prototype._getOrigin = function _getOrigin (invalidate) { var size = this.viewSize(), topLeft; if (invalidate || !this._origin) { topLeft = this.locationToLayer(this.center()); topLeft.x -= size.width / 2; topLeft.y -= size.height / 2; this._origin = this.layerToLocation(topLeft); } return this._origin; }; Map.prototype._setExtent = function _setExtent (newExtent) { var this$1 = this; var raw = Extent.create(newExtent); var se = raw.se.clone(); if (this.options.wraparound && se.lng < 0 && newExtent.nw.lng > 0) { se.lng = 180 + (180 + se.lng); } var extent = new Extent(raw.nw, se); this.center(extent.center()); var width = this.element.clientWidth; var height = this.element.clientHeight; var zoom; for (zoom = this.options.maxZoom; zoom >= this.options.minZoom; zoom--) { var topLeft = this$1.locationToLayer(extent.nw, zoom); var bottomRight = this$1.locationToLayer(extent.se, zoom); var layerWidth = math.abs(bottomRight.x - topLeft.x); var layerHeight = math.abs(bottomRight.y - topLeft.y); if (layerWidth <= width && layerHeight <= height) { break; } } this.zoom(zoom); }; Map.prototype._getExtent = function _getExtent () { var nw = this._getOrigin(); var bottomRight = this.locationToLayer(nw); var size = this.viewSize(); bottomRight.x += size.width; bottomRight.y += size.height; var se = this.layerToLocation(bottomRight); return new Extent(nw, se); }; Map.prototype._zoomAround = function _zoomAround (pivot, level) { this._setOrigin(this.layerToLocation(pivot, level), level); this.zoom(level); }; Map.prototype._initControls = function _initControls () { var controls = this.options.controls; if (controls.attribution) { this._createAttribution(controls.attribution); } if (!this.support.mobileOS) { if (controls.navigator) { this._createNavigator(controls.navigator); } if (controls.zoom) { this._createZoomControl(controls.zoom); } } }; Map.prototype._createControlElement = function _createControlElement (options, defaultPosition) { var pos = options.position || defaultPosition; var posSelector = '.' + renderPos(pos).replace(' ', '.'); var wrap = this.element.querySelector('.k-map-controls' + posSelector) || []; if (wrap.length === 0) { var div$1 = document.createElement("div"); addClass(div$1, 'k-map-controls ' + renderPos(pos)); wrap = div$1; this.element.appendChild(wrap); } var div = document.createElement("div"); wrap.appendChild(div); return div; }; Map.prototype._createAttribution = function _createAttribution (options) { var element = this._createControlElement(options, 'bottomRight'); this.attribution = new Attribution(element, options); }; Map.prototype._createNavigator = function _createNavigator (options) { var element = this._createControlElement(options, 'topLeft'); var navigator = this.navigator = new Navigator(element, deepExtend({}, options, { icons: this.options.icons })); this._navigatorPan = this._navigatorPan.bind(this); navigator.bind('pan', this._navigatorPan); this._navigatorCenter = this._navigatorCenter.bind(this); navigator.bind('center', this._navigatorCenter); }; Map.prototype._navigatorPan = function _navigatorPan (e) { var scroller = this.scroller; var x = scroller.scrollLeft + e.x; var y = scroller.scrollTop - e.y; var bounds = this._virtualSize; var width = this.element.clientWidth; var height = this.element.clientHeight; // TODO: Move limits to scroller x = limitValue(x, bounds.x.min, bounds.x.max - width); y = limitValue(y, bounds.y.min, bounds.y.max - height); this.scroller.one('scroll', proxy(this._scrollEnd, this)); this.scroller.scrollTo(-x, -y); }; Map.prototype._navigatorCenter = function _navigatorCenter () { this.center(this.options.center); }; Map.prototype._createZoomControl = function _createZoomControl (options) { var element = this._createControlElement(options, 'topLeft'); var zoomControl = this.zoomControl = new ZoomControl(element, options, this.options.icons); this._zoomControlChange = this._zoomControlChange.bind(this); zoomControl.bind('change', this._zoomControlChange); }; Map.prototype._zoomControlChange = function _zoomControlChange (e) { if (!this.trigger('zoomStart', { originalEvent: e })) { this.zoom(this.zoom() + e.delta); this.trigger('zoomEnd', { originalEvent: e }); } }; Map.prototype._initScroller = function _initScroller () { var friction = this.support.mobileOS ? FRICTION_MOBILE : FRICTION; var zoomable = this.options.zoomable !== false; var scroller = this.scroller = new Scroller(this.element.children[0], { friction: friction, velocityMultiplier: VELOCITY_MULTIPLIER, zoom: zoomable, mousewheelScrolling: false, supportDoubleTap: true }); scroller.bind('scroll', proxy(this._scroll, this)); scroller.bind('scrollEnd', proxy(this._scrollEnd, this)); scroller.userEvents.bind('gesturestart', proxy(this._scaleStart, this)); scroller.userEvents.bind('gestureend', proxy(this._scale, this)); scroller.userEvents.bind('doubleTap', proxy(this._doubleTap, this)); scroller.userEvents.bind('tap', proxy(this._tap, this)); this.scrollElement = scroller.scrollElement; }; Map.prototype._initLayers = function _initLayers () { var this$1 = this; var defs = this.options.layers, layers = this.layers = []; for (var i = 0; i < defs.length; i++) { var options = defs[i]; var layer = this$1._createLayer(options); layers.push(layer); } }; Map.prototype._createLayer = function _createLayer (options) { var type = options.type || 'shape'; var layerDefaults = this.options.layerDefaults[type]; var layerOptions = type === MARKER ? deepExtend({}, this.options.markerDefaults, options, { icons: this.options.icons }) : deepExtend({}, layerDefaults, options); var layerConstructor = layersMap[type]; var layer = new layerConstructor(this, layerOptions); if (type === MARKER) { this.markers = layer; } return layer; }; Map.prototype._createTooltip = function _createTooltip () { return new Tooltip(this.widgetService, this.options.tooltip); }; /* eslint-disable arrow-body-style */ Map.prototype._initMarkers = function _initMarkers () { var markerLayers = (this.options.layers || []).filter(function (x) { return x && x.type === MARKER; }); if (markerLayers.length > 0) { // render the markers from options.layers // instead of options.markers return; } this.markers = new MarkerLayer(this, deepExtend({}, this.options.markerDefaults, { icons: this.options.icons })); this.markers.add(this.options.markers); }; /* eslint-enable arrow-body-style */ Map.prototype._scroll = function _scroll (e) { var origin = this.locationToLayer(this._viewOrigin).round(); var movable = e.sender.movable; var offset = new g.Point(movable.x, movable.y).scale(-1).scale(1 / movable.scale); origin.x += offset.x; origin.y += offset.y; this._scrollOffset = offset; this._tooltip.offset = offset; this.hideTooltip(); this._setOrigin(this.layerToLocation(origin)); this.trigger('pan', { originalEvent: e, origin: this._getOrigin(), center: this.center() }); }; Map.prototype._scrollEnd = function _scrollEnd (e) { if (!this._scrollOffset || !this._panComplete()) { return; } this._scrollOffset = null; this._panEndTimestamp = now(); this.trigger('panEnd', { originalEvent: e, origin: this._getOrigin(), center: this.center() }); }; Map.prototype._panComplete = function _panComplete () { return now() - (this._panEndTimestamp || 0) > 50; }; Map.prototype._scaleStart = function _scaleStart (e) { if (this.trigger('zoomStart', { originalEvent: e })) { var touch = e.touches[1]; if (touch) { touch.cancel(); } } }; Map.prototype._scale = function _scale (e) { var scale = this.scroller.movable.scale; var zoom = this._scaleToZoom(scale); var gestureCenter = new g.Point(e.center.x, e.center.y); var centerLocation = this.viewToLocation(gestureCenter, zoom); var centerPoint = this.locationToLayer(centerLocation, zoom); var originPoint = centerPoint.translate(-gestureCenter.x, -gestureCenter.y); this._zoomAround(originPoint, zoom); this.trigger('zoomEnd', { originalEvent: e }); }; Map.prototype._scaleToZoom = function _scaleToZoom (scaleDelta) { var scale = this._layerSize() * scaleDelta; var tiles = scale / this.options.minSize; var zoom = math.log(tiles) / math.log(2); return math.round(zoom); }; Map.prototype._reset = function _reset () { if (this.attribution) { this.attribution.filter(this.center(), this.zoom()); } this._viewOrigin = this._getOrigin(true); this._resetScroller(); this.hideTooltip(); this.trigger('beforeReset'); this.trigger('reset'); }; Map.prototype._resetScroller = function _resetScroller () { var scroller = this.scroller; var x = scroller.dimensions.x; var y = scroller.dimensions.y; var scale = this._layerSize(); var nw = this.extent().nw; var topLeft = this.locationToLayer(nw).round(); scroller.movable.round = true; scroller.reset(); scroller.userEvents.cancel(); var zoom = this.zoom(); scroller.dimensions.forcedMinScale = pow(2, this.options.minZoom - zoom); scroller.dimensions.maxScale = pow(2, this.options.maxZoom - zoom); var xBounds = { min: -topLeft.x, max: scale - topLeft.x }; var yBounds = { min: -topLeft.y, max: scale - topLeft.y }; if (this.options.wraparound) { xBounds.max = 20 * scale; xBounds.min = -xBounds.max; } if (this.options.pannable === false) { var viewSize = this.viewSize(); xBounds.min = yBounds.min = 0; xBounds.max = viewSize.width; yBounds.max = viewSize.height; } x.makeVirtual(); y.makeVirtual(); x.virtualSize(xBounds.min, xBounds.max); y.virtualSize(yBounds.min, yBounds.max); this._virtualSize = { x: xBounds, y: yBounds }; }; // kept for API compatibility, not used Map.prototype._renderLayers = function _renderLayers () { }; Map.prototype._layerSize = function _layerSize (zoom) { var newZoom = valueOrDefault(zoom, this.options.zoom); return this.options.minSize * pow(2, newZoom); }; Map.prototype._tap = function _tap (e) { if (!this._panComplete()) { return; } var cursor = this.eventOffset(e); this.hideTooltip(); this.trigger('click', { originalEvent: e, location: this.viewToLocation(cursor) }); }; Map.prototype._doubleTap = function _doubleTap (e) { var options = this.options; if (options.zoomable !== false) { if (!this.trigger('zoomStart', { originalEvent: e })) { var toZoom = this.zoom() + DEFAULT_ZOOM_RATE; var cursor = this.eventOffset(e); var location = this.viewToLocation(cursor); var postZoom = this.locationToLayer(location, toZoom); var origin = postZoom.translate(-cursor.x, -cursor.y); this._zoomAround(origin, toZoom); this.trigger('zoomEnd', { originalEvent: e }); } } }; Map.prototype._mousewheel = function _mousewheel (e) { var delta = mousewheelDelta(e) > 0 ? -1 : 1; var options = this.options; var fromZoom = this.zoom(); var toZoom = limitValue(fromZoom + delta, options.minZoom, options.maxZoom); if (options.zoomable !== false && toZoom !== fromZoom) { if (!this.trigger('zoomStart', { originalEvent: e })) { var cursor = this.eventOffset(e); var location = this.viewToLocation(cursor); var postZoom = this.locationToLayer(location, toZoom); var origin = postZoom.translate(-cursor.x, -cursor.y); this._zoomAround(origin, toZoom); this.trigger('zoomEnd', { originalEvent: e }); } } }; Map.prototype._toDocumentCoordinates = function _toDocumentCoordinates (point) { var offset = elementOffset(this.element); return { left: round(point.x + offset.left), top: round(point.y + offset.top) }; }; return Map; }(Observable)); setDefaultOptions(Map, { name: 'Map', controls: { attribution: true, navigator: { panStep: 100 }, zoom: true }, layers: [], layerDefaults: { shape: { style: { fill: { color: '#fff' }, stroke: { color: '#aaa', width: 0.5 } } }, bubble: { style: { fill: { color: '#fff', opacity: 0.5 }, stroke: { color: '#aaa', width: 0.5 } } }, marker: { shape: 'pinTarget', tooltip: { position: 'top' } } }, center: [ 0, 0 ], icons: { type: "font", svgIcons: {} }, zoom: 3, minSize: 256, minZoom: 1, maxZoom: 19, markers: [], markerDefaults: { shape: 'pinTarget', tooltip: { position: 'top' } }, wraparound: true, // If set to true, GeoJSON layer "Point" features will be rendered as markers. // Otherwise, the points will be rendered as circles. // Defaults to `true` for KUI/jQuery, `false` everywhere else. renderPointsAsMarkers: false }); setDefaultEvents(Map, [ 'beforeReset', 'click', 'markerActivate', 'markerClick', 'markerCreated', // Events for implementing custom tooltips. 'markerMouseEnter', 'markerMouseLeave', 'pan', 'panEnd', 'reset', 'shapeClick', 'shapeCreated', 'shapeFeatureCreated', 'shapeMouseEnter', 'shapeMouseLeave', 'zoomEnd', 'zoomStart' ]); export default Map;