UNPKG

@progress/kendo-charts

Version:

Kendo UI platform-independent Charts library

396 lines (318 loc) 9.73 kB
import { geometry as g, drawing as d } from '@progress/kendo-drawing'; import { defined, last, setDefaultOptions } from '../../common'; import { proxy } from '../utils'; import { Layer } from './layer'; import { Movable } from '../scroller/draggable'; import { Location } from '../location'; const Group = d.Group; export class ShapeLayer extends Layer { constructor(map, options) { super(map, options); this._pan = proxy(this._pan, this); this.surface = d.Surface.create(this.element, { width: map.scrollElement.clientWidth, height: map.scrollElement.clientHeight }); this._initRoot(); this.movable = new Movable(this.surface.element); this._markers = []; this._click = this._handler('shapeClick'); this.surface.bind('click', this._click); this._mouseleave = this._handler('shapeMouseLeave'); this.surface.bind('mouseleave', this._mouseleave); this.surface.bind('mouseenter', this._mouseenter.bind(this)); } destroy() { super.destroy(); this.surface.destroy(); } _reset() { super._reset(); this._translateSurface(); this._data = this._readData(); if (this._hasData()) { this._load(this._data); } } _initRoot() { this._root = new Group(); this.surface.draw(this._root); } _beforeReset() { this.surface.clear(); this._initRoot(); } _resize() { this.surface.size(this.map.size()); } _readData() { const data = super._readData(); if (data.type === "FeatureCollection") { return data.features; } if (data.type === "GeometryCollection") { return data.geometries; } return data; } _load(data) { this._data = data; this._clearMarkers(); if (!this._loader) { this._loader = new GeoJsonLoader(this.map, this.options.style, this); } let container = new Group(); for (let i = 0; i < data.length; i++) { let shape = this._loader.parse(data[i]); if (shape) { container.append(shape); } } this._root.clear(); this._root.append(container); } shapeCreated(shape) { let cancelled = false; // the GeoJSON loader builds "Point" type as a circle // use the circle shape type as and indicator for rendering a marker // keep the behavior under a setting as this is supported by kendo jQuery Map // but we opted out of this in blazor if (shape instanceof d.Circle && this.map.options.renderPointsAsMarkers) { cancelled = defined(this._createMarker(shape)); } if (!cancelled) { let args = { layer: this, shape: shape }; cancelled = this.map.trigger('shapeCreated', args); } return cancelled; } featureCreated(e) { e.layer = this; this.map.trigger('shapeFeatureCreated', e); } _createMarker(shape) { let marker = this.map.markers.bind({ location: shape.location }, shape.dataItem); if (marker) { this._markers.push(marker); } return marker; } _clearMarkers() { for (let i = 0; i < this._markers.length; i++) { this.map.markers.remove(this._markers[i]); } this._markers = []; } _pan() { if (!this._panning) { this._panning = true; this.surface.suspendTracking(); } } _panEnd(e) { super._panEnd(e); this._translateSurface(); this.surface.resumeTracking(); this._panning = false; } _translateSurface() { let map = this.map; let nw = map.locationToView(map.extent().nw); if (this.surface.translate) { this.surface.translate(nw); this.movable.moveTo({ x: nw.x, y: nw.y }); } } _eventArgs(e) { return { layer: this, layerIndex: this._layerIndex(), shape: e.element, shapeIndex: (this._data || []).indexOf(e.element.dataItem), originalEvent: e.originalEvent }; } _handler(eventName) { return (e) => { if (e.element) { this.map.trigger(eventName, this._eventArgs(e)); } }; } _mouseenter(e) { if (!e.element) { return; } this.map.trigger('shapeMouseEnter', this._eventArgs(e)); const shape = e.element; const anchor = this._tooltipAnchor(e); this.map._tooltip.show(anchor, this._tooltipContext(shape)); } _tooltipContext(shape) { return { type: 'shape', layerIndex: this._layerIndex(), className: 'k-map-shape-tooltip', dataItem: shape.dataItem, location: shape.location }; } _tooltipAnchor(e) { const cursor = this.map.eventOffset(e.originalEvent); return { top: cursor.y, left: cursor.x }; } _activate() { super._activate(); this._panHandler = proxy(this._pan, this); this.map.bind('pan', this.panHandler); } _deactivate() { super._deactivate(); this.map.unbind('pan', this._panHandler); } } setDefaultOptions(ShapeLayer, { autoBind: true, zIndex: 100 }); class GeoJsonLoader { constructor(locator, defaultStyle, observer) { this.observer = observer; this.locator = locator; this.style = defaultStyle; } parse(item) { let root = new Group(); let unwrap = true; if (item.type === 'Feature') { unwrap = false; this._loadGeometryTo(root, item.geometry, item); this._featureCreated(root, item); } else { this._loadGeometryTo(root, item, item); } if (unwrap && root.children.length < 2) { root = root.children[0]; } return root; } _shapeCreated(shape) { let cancelled = false; if (this.observer && this.observer.shapeCreated) { cancelled = this.observer.shapeCreated(shape); } return cancelled; } _featureCreated(group, dataItem) { if (this.observer && this.observer.featureCreated) { this.observer.featureCreated({ group: group, dataItem: dataItem, properties: dataItem.properties }); } } _loadGeometryTo(container, geometry, dataItem) { let coords = geometry.coordinates; let i; let path; switch (geometry.type) { case 'LineString': path = this._loadPolygon(container, [coords], dataItem); this._setLineFill(path); break; case 'MultiLineString': for (i = 0; i < coords.length; i++) { path = this._loadPolygon(container, [coords[i]], dataItem); this._setLineFill(path); } break; case 'Polygon': this._loadPolygon(container, coords, dataItem); break; case 'MultiPolygon': for (i = 0; i < coords.length; i++) { this._loadPolygon(container, coords[i], dataItem); } break; case 'Point': this._loadPoint(container, coords, dataItem); break; case 'MultiPoint': for (i = 0; i < coords.length; i++) { this._loadPoint(container, coords[i], dataItem); } break; default: break; } } _setLineFill(path) { let segments = path.segments; if (segments.length < 4 || !segments[0].anchor().equals(last(segments).anchor())) { path.options.fill = null; } } _loadShape(container, shape) { if (!this._shapeCreated(shape)) { container.append(shape); } return shape; } _loadPolygon(container, rings, dataItem) { let shape = this._buildPolygon(rings); shape.dataItem = dataItem; shape.location = this.locator.viewToLocation(shape.bbox().center()); return this._loadShape(container, shape); } _buildPolygon(rings) { let type = rings.length > 1 ? d.MultiPath : d.Path; let path = new type(this.style); for (let i = 0; i < rings.length; i++) { for (let j = 0; j < rings[i].length; j++) { let point = this.locator.locationToView(Location.fromLngLat(rings[i][j])); if (j === 0) { path.moveTo(point.x, point.y); } else { path.lineTo(point.x, point.y); } } } return path; } _loadPoint(container, coords, dataItem) { let location = Location.fromLngLat(coords); let point = this.locator.locationToView(location); let circle = new g.Circle(point, 10); let shape = new d.Circle(circle, this.style); shape.dataItem = dataItem; shape.location = location; return this._loadShape(container, shape); } }