UNPKG

leaflet-labeled-circle

Version:

Special type of SVG marker with a label inside and draggable around the anchor point

281 lines (225 loc) 6.06 kB
const L = require('leaflet'); const Circle = require('./circle'); require('leaflet-path-drag'); const LabeledMarker = L.FeatureGroup.extend({ options: { /** * @param {LabeledMarker} marker * @param {Object} feature * @return {String} */ getLabelText: (marker, feature) => feature.properties.text, /** * @param {LabeledMarker} marker * @param {Object} feature * @param {L.LatLng} latlng * @return {L.LatLng} */ getLabelPosition: (marker, feature, latlng) => { return feature.properties.labelPosition ? L.latLng(feature.properties.labelPosition.slice().reverse()) : latlng; }, labelPositionKey: 'labelPosition', markerOptions: { color: '#f00', fillOpacity: 0.75, draggable: true, radius: 15 }, anchorOptions: { color: '#00f', radius: 3 }, lineOptions: { color: '#f00', dashArray: [2, 6], lineCap: 'square', weight: 2 } }, /** * @class LabeledMarker * @constructor * @extends {L.FeatureGroup} * * @param {L.LatLng} latlng * @param {Object=} feature * @param {Object=} options */ initialize(latlng, feature, options) { L.Util.setOptions(this, options); /** * @type {Object} */ this.feature = feature || { type: 'Feature', properties: {}, geometry: { 'type': 'Point' } }; /** * @type {L.LatLng} */ this._latlng = latlng; /** * @type {CircleLabel} */ this._marker = null; /** * @type {L.CircleMarker} */ this._anchor = null; /** * @type {L.Polyline} */ this._line = null; /** * @type {L.Point} */ this._initialDistance = null; this._createLayers(); L.LayerGroup.prototype.initialize.call(this, [this._anchor, this._line, this._marker]); }, /** * @return {L.LatLng} */ getLabelPosition() { return this._marker.getLatLng(); }, /** * @return {L.LatLng} */ getLatLng() { return this._latlng; }, /** * Serialize * @return {Object} */ toGeoJSON(geometryCollection) { const feature = L.GeoJSON.getFeature(this, { type: 'Point', coordinates: L.GeoJSON.latLngToCoords(this._anchor.getLatLng()) }); feature.properties[this.options.labelPositionKey] = L.GeoJSON.latLngToCoords(this._marker.getLatLng()); feature.properties.text = this._marker.getText(); return geometryCollection ? L.LabeledCircleMarker .toGeometryCollection(feature, this.options.labelPositionKey) : feature; }, /** * @param {String} text * @return {LabeledMarker} */ setText(text) { this._marker.setText(text); return this; }, /** * Creates anchor, line and label */ _createLayers() { const opts = this.options; const pos = opts.getLabelPosition(this, this.feature, this._latlng); const text = opts.getLabelText(this, this.feature); if ('draggable' in opts) { opts.markerOptions.draggable = opts.draggable; } this._marker = new Circle(text, pos, L.Util.extend({ interactive: this.options.interactive }, LabeledMarker.prototype.options.markerOptions, opts.markerOptions) ).on('drag', this._onMarkerDrag, this) .on('dragstart', this._onMarkerDragStart, this) .on('dragend', this._onMarkerDragEnd, this); this._anchor = new L.CircleMarker(this._latlng, L.Util.extend({}, LabeledMarker.prototype.options.anchorOptions, opts.anchorOptions)); this._line = new L.Polyline([this._latlng, this._marker.getLatLng()], L.Util.extend({}, LabeledMarker.prototype.options.lineOptions, opts.lineOptions)); }, /** * Store shift to be precise while dragging * @param {Event} evt */ _onMarkerDragStart(evt) { this._initialDistance = L.DomEvent.getMousePosition(evt) .subtract(this._map.latLngToContainerPoint(this._marker.getLatLng())); this.fire('label:' + evt.type, evt); //L.Util.requestAnimFrame(this._marker.bringToFront, this._marker); }, /** * Line dragging * @param {DragEvent} evt */ _onMarkerDrag(evt) { const latlng = this._map.containerPointToLatLng( L.DomEvent.getMousePosition(evt)._subtract(this._initialDistance)); this._line.setLatLngs([latlng, this._latlng]); this.fire('label:' + evt.type, evt); }, _onMarkerDragEnd(evt) { this._line.setLatLngs([this._marker.getLatLng(), this._latlng]); this.fire('label:' + evt.type, evt); }, enableDragging() { if (this._marker.dragging) this._marker.dragging.enable(); return this; }, disableDragging() { if (this._marker.dragging) this._marker.dragging.disable(); return this; } }); /** * @param {Object} feature * @param {String=} key * @return {Object} */ function toGeometryCollection(feature, key) { key = key || 'labelPosition'; const anchorPos = feature.geometry.coordinates.slice(); let labelPos = feature.properties[key]; if (!labelPos) throw new Error('No label position set'); labelPos = labelPos.slice(); const geometries = [{ type: 'Point', coordinates: anchorPos }, { type: 'LineString', coordinates: [ anchorPos.slice(), labelPos ] }, { type: 'Point', coordinates: labelPos.slice() }, { type: 'Point', coordinates: labelPos.slice() }]; return { type: 'Feature', properties: L.Util.extend({}, feature.properties, { geometriesTypes: ['anchor', 'connection', 'label', 'textbox'] }), bbox: feature.bbox, geometry: { type: 'GeometryCollection', geometries: geometries } }; } LabeledMarker.toGeometryCollection = toGeometryCollection; L.LabeledCircleMarker = LabeledMarker; L.labeledCircleMarker = (latlng, feature, options) => { return new LabeledMarker(latlng, feature, options); }; module.exports = LabeledMarker;