nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

commit 0dbeead3aff7b95c75728b650697734caaf3f4e1
parent 6a74ae81795db22b22d75848ff0d5a5ab7f35842
Author: Sergej Orlov <wladimirych@gmail.com>
Date:   Thu,  1 Nov 2018 22:25:53 +0100

Merge branch '140-fix-markers-paths-jump' into release-5

Diffstat:
Msrc/lib/leaflet.control.azimuth/index.js | 18+++++++++++++++---
Asrc/lib/leaflet.fixes/fixWorldCopyJump.js | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/leaflet.fixes/index.js | 3+++
Msrc/lib/leaflet.polyline-edit/index.js | 35++++++++++++++++++++++++++---------
Msrc/lib/leaflet.polyline-measure/index.js | 30+++++++++++++++++++++++++-----
5 files changed, 208 insertions(+), 17 deletions(-)

diff --git a/src/lib/leaflet.control.azimuth/index.js b/src/lib/leaflet.control.azimuth/index.js @@ -65,20 +65,32 @@ L.Control.Azimuth = L.Control.extend({ const iconSingle = L.icon({iconUrl: iconPointer, iconSize: [30, 30]}); const iconStart = L.icon({iconUrl: iconPointerStart, iconSize: [30, 30]}); const iconEnd = L.icon({iconUrl: iconPointerEnd, iconSize: [30, 45]}); + this.azimuthLine = L.polyline([], {interactive: false, weight: 1.5}); this.markers = { single: L.marker([0, 0], {icon: iconSingle, draggable: true, which: 'start'}) .on('drag', this.onMarkerDrag, this) .on('click', L.DomEvent.stopPropagation), - start: L.marker([0, 0], {icon: iconStart, draggable: true, which: 'start', rotationOrigin: 'center center'}) + start: L.marker([0, 0], { + icon: iconStart, + draggable: true, + which: 'start', + rotationOrigin: 'center center', + projectedShift: () => this.azimuthLine.shiftProjectedFitMapView() + }) .on('drag', this.onMarkerDrag, this) .on('click', L.DomEvent.stopPropagation) .on('dragend', this.onMarkerDragEnd, this), - end: L.marker([0, 0], {icon: iconEnd, draggable: true, which: 'end', rotationOrigin: 'center center'}) + end: L.marker([0, 0], { + icon: iconEnd, + draggable: true, + which: 'end', + rotationOrigin: 'center center', + projectedShift: () => this.azimuthLine.shiftProjectedFitMapView() + }) .on('drag', this.onMarkerDrag, this) .on('click', L.DomEvent.stopPropagation) .on('dragend', this.onMarkerDragEnd, this) }; - this.azimuthLine = L.polyline([], {interactive: false, weight: 1.5}); }, onAdd: function(map) { diff --git a/src/lib/leaflet.fixes/fixWorldCopyJump.js b/src/lib/leaflet.fixes/fixWorldCopyJump.js @@ -0,0 +1,138 @@ +import L from 'leaflet'; + +function wrapLongitudeToTarget(latLng, targetLatLng) { + const targetLng = targetLatLng.lng; + const lng = latLng.lng; + let newLng; + if (Math.abs(lng + 360 - targetLng) < Math.abs(lng - targetLng)) { + newLng = lng + 360; + } else if (Math.abs(lng - 360 - targetLng) < Math.abs(lng - targetLng)) { + newLng = lng - 360; + } else { + return latLng; + } + return L.latLng(latLng.lat, newLng); +} + +function fixVectorMarkerWorldJump() { + L.Polyline.prototype.shiftProjectedFitMapView = function() { + const polylineBounds = this.getBounds(); + let shift = null; + if (this._map && polylineBounds.isValid()) { + const worldWidth = this._map.getPixelWorldBounds().getSize().x; + const polylineCenter = polylineBounds.getCenter(); + const mapCenter = this._map.getCenter(); + + if (polylineCenter.lng < mapCenter.lng - 180) { + shift = worldWidth + } else if (polylineCenter.lng > mapCenter.lng + 180) { + shift = -worldWidth; + } else { + shift = 0; + } + } + return shift; + }; + + + // Shift line points longitude by +360 or -360, to minimize distance between line center and map view center + // Longitude is changed only for display, longitude of pints is not changed + // Breaks dipslay of lines spanning more then one world copy + L.Polyline.prototype._projectLatlngs = function(latlngs, result, projectedBounds) { + var flat = latlngs[0] instanceof L.LatLng, + len = latlngs.length, + i, ring; + let shift = null; + if (this.options.projectedShift) { + shift = this.options.projectedShift(); + } + if (shift === null) { + shift = this.shiftProjectedFitMapView(); + } + + if (flat) { + ring = []; + for (i = 0; i < len; i++) { + let p = this._map.latLngToLayerPoint(latlngs[i]); + p.x += shift; + ring[i] = p; + projectedBounds.extend(p); + } + result.push(ring); + } else { + for (i = 0; i < len; i++) { + this._projectLatlngs(latlngs[i], result, projectedBounds); + } + } + }; + + // Shift marker longitude by +360 or -360, which is closer to map view center + // Longitude is changed only for positioning html-element, Marker._latlng is not changed + // Breaks display of markers with huge longitudes like 750 (can be displayed only at zoom levels 0 or 1) + L.Marker.prototype.update = function() { + if (this._icon) { + var pos = this._map.latLngToLayerPoint(this._latlng).round(); + let shift = null; + if (this.options.projectedShift) { + shift = this.options.projectedShift(); + } + if (shift === null) { + const mapCenter = this._map.getCenter(); + const worldWidth = this._map.getPixelWorldBounds().getSize().x; + if (this._latlng.lng < mapCenter.lng - 180) { + shift = worldWidth; + } else if (this._latlng.lng > mapCenter.lng + 180) { + shift = -worldWidth; + } else { + shift = 0; + } + } + pos.x += shift; + this._setPos(pos); + } + + return this; + }; + + // Emit viewreset event when longitude of map view center changes more then 90 degrees from prevoius reset + L.Map.addInitHook(function() { + this._lastResetLongitude = null; + this.on('viewreset', () => { + this._lastResetLongitude = this.getCenter().lng; + }); + + this.on('move', (e) => { + const lng = this.getCenter().lng; + if (this._lastResetLongitude === null) { + this._lastResetLongitude = lng; + } else if (Math.abs(lng - this._lastResetLongitude) > 90) { + this.fire('viewreset'); + } + }) + }); + + // Avoid marker longitude change from 180 to -180 while dragging. + L.Handler.MarkerDrag.prototype._onDrag = function(e) { + var marker = this._marker, + shadow = marker._shadow, + iconPos = L.DomUtil.getPosition(marker._icon), + latlng = marker._map.layerPointToLatLng(iconPos); + // update shadow position + if (shadow) { + L.DomUtil.setPosition(shadow, iconPos); + } + + latlng = wrapLongitudeToTarget(latlng, marker._latlng); + marker._latlng = latlng; + e.latlng = latlng; + e.oldLatLng = this._oldLatLng; + + // @event drag: Event + // Fired repeatedly while the user drags the marker. + marker + .fire('move', e) + .fire('drag', e); + } +} + +export {wrapLongitudeToTarget, fixVectorMarkerWorldJump} +\ No newline at end of file diff --git a/src/lib/leaflet.fixes/index.js b/src/lib/leaflet.fixes/index.js @@ -1,11 +1,13 @@ import L from 'leaflet'; import './style.css'; +import {fixVectorMarkerWorldJump} from './fixWorldCopyJump'; function fixAll() { fixPanAnimationBug(); fixTouchDetection(); fixMapKeypressEvent(); fixVectorDrawWhileAnimation(); + fixVectorMarkerWorldJump() } // https://github.com/Leaflet/Leaflet/issues/3575 @@ -78,4 +80,5 @@ function fixVectorDrawWhileAnimation() { L.Renderer.__animationFixed = true; } + export {fixAll} diff --git a/src/lib/leaflet.polyline-edit/index.js b/src/lib/leaflet.polyline-edit/index.js @@ -1,5 +1,6 @@ import L from 'leaflet'; import './edit_line.css'; +import {wrapLongitudeToTarget} from 'lib/leaflet.fixes/fixWorldCopyJump'; L.Polyline.EditMixinOptions = { className: 'leaflet-editable-line' @@ -85,8 +86,16 @@ L.Polyline.EditMixin = { onMapClick: function(e) { if (this._drawingDirection) { - var newNodeIndex = this._drawingDirection === -1 ? 1 : this.getLatLngs().length - 1; - this.addNode(newNodeIndex, e.latlng); + let newNodeIndex, + refNodeIndex; + if (this._drawingDirection === -1) { + newNodeIndex = 1; + refNodeIndex = 0; + } else { + newNodeIndex = this._latlngs.length - 1; + refNodeIndex = this._latlngs.length - 1; + } + this.addNode(newNodeIndex, wrapLongitudeToTarget(e.latlng, this._latlngs[refNodeIndex])); } else { if (!this.preventStopEdit) { this.stopEdit(true); @@ -177,18 +186,21 @@ L.Polyline.EditMixin = { onMouseMoveFollowEndNode: function(e) { var nodeIndex = this._drawingDirection === -1 ? 0 : this.getLatLngs().length - 1; - this.spliceLatLngs(nodeIndex, 1, e.latlng); + let latlng = e.latlng; + if (this._latlngs.length > 0) { + latlng = wrapLongitudeToTarget(latlng, this._latlngs[nodeIndex]); + } + this.spliceLatLngs(nodeIndex, 1, latlng); this.fire('nodeschanged'); }, makeNodeMarker: function(nodeIndex) { var node = this.getLatLngs()[nodeIndex], marker = L.marker(node.clone(), { - icon: L.divIcon( - {className: 'line-editor-node-marker-halo', 'html': '<div class="line-editor-node-marker"></div>'} - ), + icon: L.divIcon({className: 'line-editor-node-marker-halo', 'html': '<div class="line-editor-node-marker"></div>'}), draggable: true, - zIndexOffset: this._nodeMarkersZOffset + zIndexOffset: this._nodeMarkersZOffset, + projectedShift: () => this.shiftProjectedFitMapView() } ); marker @@ -235,7 +247,11 @@ L.Polyline.EditMixin = { if (!p2) { return; } - const segmentOverlay = L.polyline([p1, p2], {weight: 10, opacity: 0.0}); + const segmentOverlay = L.polyline([p1, p2], { + weight: 10, + opacity: 0.0, + projectedShift: () => this.shiftProjectedFitMapView() + }); segmentOverlay.on('mousedown', this.onSegmentMouseDownAddNode, this); segmentOverlay.on('contextmenu', function(e) { this.stopDrawingLine(); @@ -259,7 +275,8 @@ L.Polyline.EditMixin = { var segmentOverlay = e.target, latlngs = this.getLatLngs(), nodeIndex = latlngs.indexOf(segmentOverlay._lineNode) + 1; - this.addNode(nodeIndex, e.latlng); + const midPoint = L.latLngBounds(latlngs[nodeIndex], latlngs[nodeIndex - 1]).getCenter(); + this.addNode(nodeIndex, wrapLongitudeToTarget(e.latlng, midPoint)); if (L.Draggable._dragging) { L.Draggable._dragging.finishDrag() } diff --git a/src/lib/leaflet.polyline-measure/index.js b/src/lib/leaflet.polyline-measure/index.js @@ -32,7 +32,8 @@ L.MeasuredLine = L.Polyline.extend({ this._ticks = {}; this.updateTicks(); this._map.on('zoomend', this.updateTicks, this); - this._map.on('dragend', this.updateTicks, this); + // markers are created only for visible part of map, need to update when it changes + this._map.on('moveend', this.updateTicks, this); this.on('nodeschanged', this.updateTicksLater, this); }, @@ -42,7 +43,7 @@ L.MeasuredLine = L.Polyline.extend({ onRemove: function(map) { this._map.off('zoomend', this.updateTicks, this); - this._map.off('dragend', this.updateTicks, this); + this._map.off('moveend', this.updateTicks, this); this.off('nodeschanged', this.updateTicks, this); this._clearTicks(); L.Polyline.prototype.onRemove.call(this, map); @@ -69,7 +70,12 @@ L.MeasuredLine = L.Polyline.extend({ className: 'measure-tick-icon' } ); - marker = L.marker(tick.position, {icon: icon, interactive: false, keyboard: false}); + marker = L.marker(tick.position, { + icon: icon, + interactive: false, + keyboard: false, + projectedShift: () => this.shiftProjectedFitMapView(), + }); marker.addTo(this._map); } this._ticks[tick.distanceValue.toString()] = marker; @@ -84,10 +90,24 @@ L.MeasuredLine = L.Polyline.extend({ var steps = [500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000]; var ticks = []; + const self = this; function addTick(position, segment, distanceValue) { - if (bounds && (!bounds.contains(position))) { - return; + if (bounds) { + // create markers only in visible part of map + const normalizedBounds = self._map.wrapLatLngBounds(bounds); + const normalizedPosition = position.wrap(); + // account for worldCopyJump + const positionMinus360 = L.latLng(normalizedPosition.lat, normalizedPosition.lng - 360); + const positionPlus360 = L.latLng(normalizedPosition.lat, normalizedPosition.lng + 360); + if ( + !normalizedBounds.contains(normalizedPosition) && + !normalizedBounds.contains(positionMinus360) && + !normalizedBounds.contains(positionPlus360) + ) { + return; + } } + var sinCos = sinCosFromLatLonSegment(segment), sin = sinCos[0], cos = sinCos[1], transformMatrix;