@progress/kendo-charts
Version:
Kendo UI platform-independent Charts library
982 lines (776 loc) • 27.9 kB
JavaScript
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;