all these changes

This commit is contained in:
Jake Kasper
2026-04-09 13:19:47 -05:00
parent e83a51a051
commit 65315f36d1
39102 changed files with 7932979 additions and 567 deletions

View File

@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet.draw drawing and editing tools</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="libs/leaflet.css"/>
<link rel="stylesheet" href="../../src/leaflet.draw.css"/>
<script src="libs/leaflet-src.js"></script>
<script src="../../src/Leaflet.draw.js"></script>
<script src="../../src/Leaflet.Draw.Event.js"></script>
<script src="../../src/edit/handler/Edit.Poly.js"></script>
<script src="../../src/edit/handler/Edit.SimpleShape.js"></script>
<script src="../../src/edit/handler/Edit.Rectangle.js"></script>
<script src="../../src/edit/handler/Edit.Marker.js"></script>
<script src="../../src/edit/handler/Edit.CircleMarker.js"></script>
<script src="../../src/edit/handler/Edit.Circle.js"></script>
<script src="../../src/draw/handler/Draw.Feature.js"></script>
<script src="../../src/draw/handler/Draw.Polyline.js"></script>
<script src="../../src/draw/handler/Draw.Polygon.js"></script>
<script src="../../src/draw/handler/Draw.SimpleShape.js"></script>
<script src="../../src/draw/handler/Draw.Rectangle.js"></script>
<script src="../../src/draw/handler/Draw.Circle.js"></script>
<script src="../../src/draw/handler/Draw.Marker.js"></script>
<script src="../../src/draw/handler/Draw.CircleMarker.js"></script>
<script src="../../src/ext/TouchEvents.js"></script>
<script src="../../src/ext/LatLngUtil.js"></script>
<script src="../../src/ext/GeometryUtil.js"></script>
<script src="../../src/ext/LineUtil.Intersect.js"></script>
<script src="../../src/ext/Polyline.Intersect.js"></script>
<script src="../../src/ext/Polygon.Intersect.js"></script>
<script src="../../src/Control.Draw.js"></script>
<script src="../../src/Tooltip.js"></script>
<script src="../../src/Toolbar.js"></script>
<script src="../../src/draw/DrawToolbar.js"></script>
<script src="../../src/edit/EditToolbar.js"></script>
<script src="../../src/edit/handler/EditToolbar.Edit.js"></script>
<script src="../../src/edit/handler/EditToolbar.Delete.js"></script>
</head>
<body>
<div id="map" style="width: 800px; height: 600px; border: 1px solid #ccc"></div>
<button id="changeColor">Rectangle -> Blue</button>
<script>
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
osm = L.tileLayer(osmUrl, {maxZoom: 18, attribution: osmAttrib}),
map = new L.Map('map', {layers: [osm], center: new L.LatLng(-37.7772, 175.2756), zoom: 15});
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
// Set the title to show on the polygon button
L.drawLocal.draw.toolbar.buttons.polygon = 'Draw a sexy polygon!';
var drawControl = new L.Control.Draw({
position: 'topright',
draw: {
polyline: false,
polygon: false,
circle: false,
marker: true
},
edit: {
featureGroup: drawnItems,
remove: true
}
});
map.addControl(drawControl);
map.on(L.Draw.Event.CREATED, function (e) {
var type = e.layerType,
layer = e.layer;
if (type === 'marker') {
layer.bindPopup('A popup!');
}
drawnItems.addLayer(layer);
});
map.on(L.Draw.Event.EDITED, function (e) {
var layers = e.layers;
var countOfEditedLayers = 0;
layers.eachLayer(function (layer) {
countOfEditedLayers++;
});
console.log("Edited " + countOfEditedLayers + " layers");
});
L.DomUtil.get('changeColor').onclick = function () {
drawControl.setDrawingOptions({rectangle: {shapeOptions: {color: '#004a80'}}});
};
</script>
</body>
</html>

View File

@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet.draw vector editing handlers</title>
<link rel="stylesheet" href="libs/leaflet.css" />
<script src="libs/leaflet-src.js"></script>
<script src="../../src/Leaflet.draw.js"></script>
<script src="../../src/Leaflet.Draw.Event.js"></script>
<script src="../../src/ext/TouchEvents.js"></script>
<script src="../../src/edit/handler/Edit.Poly.js"></script>
<script src="../../src/edit/handler/Edit.SimpleShape.js"></script>
<script src="../../src/edit/handler/Edit.Rectangle.js"></script>
<script src="../../src/edit/handler/Edit.Marker.js"></script>
<script src="../../src/edit/handler/Edit.CircleMarker.js"></script>
<script src="../../src/edit/handler/Edit.Circle.js"></script>
</head>
<body>
<div id="map" style="width: 800px; height: 600px; border: 1px solid #ccc"></div>
<script>
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
osm = L.tileLayer(osmUrl, {maxZoom: 18, attribution: osmAttrib});
map = new L.Map('map', {layers: [osm], center: new L.LatLng(51.505, -0.04), zoom: 13});
var polygon = new L.Polygon([
[51.51, -0.1],
[51.5, -0.06],
[51.52, -0.03]
]);
polygon.editing.enable();
map.addLayer(polygon);
var polyline = new L.Polyline([
[51.50, -0.04],
[51.49, -0.02],
[51.51, 0],
[51.52, -0.02]
]);
polyline.editing.enable();
map.addLayer(polyline);
var circleMarker = L.circleMarker([51.50, -0.08]);
circleMarker.editing.enable();
map.addLayer(circleMarker);
var circle = L.circle([51.53, -0.06], 600);
circle.editing.enable();
map.addLayer(circle);
var rectangle = L.rectangle([[51.49, -0.1], [51.48, -0.06]]);
rectangle.editing.enable();
map.addLayer(rectangle);
polygon.on('edit', function() {
console.log('Polygon was edited!');
});
polyline.on('edit', function() {
console.log('Polyline was edited!');
});
</script>
</body>
</html>

View File

@@ -0,0 +1,85 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet.draw vector editing handlers</title>
<script src="libs/leaflet-src.js"></script>
<link rel="stylesheet" href="libs/leaflet.css"/>
<script src="../../src/Leaflet.draw.js"></script>
<script src="../../src/Leaflet.Draw.Event.js"></script>
<link rel="stylesheet" href="../../src/leaflet.draw.css"/>
<script src="../../src/Toolbar.js"></script>
<script src="../../src/Tooltip.js"></script>
<script src="../../src/ext/GeometryUtil.js"></script>
<script src="../../src/ext/LatLngUtil.js"></script>
<script src="../../src/ext/LineUtil.Intersect.js"></script>
<script src="../../src/ext/Polygon.Intersect.js"></script>
<script src="../../src/ext/Polyline.Intersect.js"></script>
<script src="../../src/ext/TouchEvents.js"></script>
<script src="../../src/draw/DrawToolbar.js"></script>
<script src="../../src/draw/handler/Draw.Feature.js"></script>
<script src="../../src/draw/handler/Draw.SimpleShape.js"></script>
<script src="../../src/draw/handler/Draw.Polyline.js"></script>
<script src="../../src/draw/handler/Draw.Marker.js"></script>
<script src="../../src/draw/handler/Draw.Circle.js"></script>
<script src="../../src/draw/handler/Draw.CircleMarker.js"></script>
<script src="../../src/draw/handler/Draw.Polygon.js"></script>
<script src="../../src/draw/handler/Draw.Rectangle.js"></script>
<script src="../../src/edit/EditToolbar.js"></script>
<script src="../../src/edit/handler/EditToolbar.Edit.js"></script>
<script src="../../src/edit/handler/EditToolbar.Delete.js"></script>
<script src="../../src/Control.Draw.js"></script>
<script src="../../src/edit/handler/Edit.Poly.js"></script>
<script src="../../src/edit/handler/Edit.SimpleShape.js"></script>
<script src="../../src/edit/handler/Edit.Rectangle.js"></script>
<script src="../../src/edit/handler/Edit.Marker.js"></script>
<script src="../../src/edit/handler/Edit.CircleMarker.js"></script>
<script src="../../src/edit/handler/Edit.Circle.js"></script>
</head>
<body>
<div id="map" style="width: 800px; height: 600px; border: 1px solid #ccc"></div>
<script>
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
osm = L.tileLayer(osmUrl, { maxZoom: 18, attribution: osmAttrib }),
map = new L.Map('map', { center: new L.LatLng(51.505, -0.04), zoom: 13 }),
drawnItems = L.featureGroup().addTo(map);
L.control.layers({
'osm': osm.addTo(map),
"google": L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', {
attribution: 'google'
})
}, { 'drawlayer': drawnItems }, { position: 'topleft', collapsed: false }).addTo(map);
map.addControl(new L.Control.Draw({
edit: {
featureGroup: drawnItems,
poly: {
allowIntersection: false
}
},
draw: {
polygon: {
allowIntersection: false,
showArea: true
}
}
}));
map.on(L.Draw.Event.CREATED, function (event) {
var layer = event.layer;
drawnItems.addLayer(layer);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,894 @@
/**
* Matrix transform path for SVG/VML
* TODO: adapt to Leaflet 0.8 upon release
*/
"use strict";
if (L.Browser.svg) { // SVG transformation
L.Path.include({
/**
* Reset transform matrix
*/
_resetTransform: function() {
this._container.setAttributeNS(null, 'transform', '');
},
/**
* Applies matrix transformation to SVG
* @param {Array.<Number>} matrix
*/
_applyTransform: function(matrix) {
this._container.setAttributeNS(null, "transform",
'matrix(' + matrix.join(' ') + ')');
}
});
} else { // VML transform routines
L.Path.include({
/**
* Reset transform matrix
*/
_resetTransform: function() {
if (this._skew) {
// super important! workaround for a 'jumping' glitch:
// disable transform before removing it
this._skew.on = false;
this._container.removeChild(this._skew);
this._skew = null;
}
},
/**
* Applies matrix transformation to VML
* @param {Array.<Number>} matrix
*/
_applyTransform: function(matrix) {
var skew = this._skew;
if (!skew) {
skew = this._createElement('skew');
this._container.appendChild(skew);
skew.style.behavior = 'url(#default#VML)';
this._skew = skew;
}
// handle skew/translate separately, cause it's broken
var mt = matrix[0].toFixed(8) + " " + matrix[1].toFixed(8) + " " +
matrix[2].toFixed(8) + " " + matrix[3].toFixed(8) + " 0 0";
var offset = Math.floor(matrix[4]).toFixed() + ", " +
Math.floor(matrix[5]).toFixed() + "";
var s = this._container.style;
var l = parseFloat(s.left);
var t = parseFloat(s.top);
var w = parseFloat(s.width);
var h = parseFloat(s.height);
if (isNaN(l)) l = 0;
if (isNaN(t)) t = 0;
if (isNaN(w) || !w) w = 1;
if (isNaN(h) || !h) h = 1;
var origin = (-l / w - 0.5).toFixed(8) + " " + (-t / h - 0.5).toFixed(8);
skew.on = "f";
skew.matrix = mt;
skew.origin = origin;
skew.offset = offset;
skew.on = true;
}
});
}
// Renderer-independent
L.Path.include({
/**
* Check if the feature was dragged, that'll supress the click event
* on mouseup. That fixes popups for example
*
* @param {MouseEvent} e
*/
_onMouseClick: function(e) {
if ((this.dragging && this.dragging.moved()) ||
(this._map.dragging && this._map.dragging.moved())) {
return;
}
this._fireMouseEvent(e);
}
});
/**
* Leaflet vector features drag functionality
* @preserve
*/
"use strict";
/**
* Drag handler
* @class L.Path.Drag
* @extends {L.Handler}
*/
L.Handler.PathDrag = L.Handler.extend( /** @lends L.Path.Drag.prototype */ {
/**
* @param {L.Path} path
* @constructor
*/
initialize: function(path) {
/**
* @type {L.Path}
*/
this._path = path;
/**
* @type {Array.<Number>}
*/
this._matrix = null;
/**
* @type {L.Point}
*/
this._startPoint = null;
/**
* @type {L.Point}
*/
this._dragStartPoint = null;
},
/**
* Enable dragging
*/
addHooks: function() {
this._path.on('mousedown', this._onDragStart, this);
L.DomUtil.addClass(this._path._container, 'leaflet-path-draggable');
},
/**
* Disable dragging
*/
removeHooks: function() {
this._path.off('mousedown', this._onDragStart, this);
L.DomUtil.removeClass(this._path._container, 'leaflet-path-draggable');
},
/**
* @return {Boolean}
*/
moved: function() {
return this._path._dragMoved;
},
/**
* Start drag
* @param {L.MouseEvent} evt
*/
_onDragStart: function(evt) {
this._startPoint = evt.containerPoint.clone();
this._dragStartPoint = evt.containerPoint.clone();
this._matrix = [1, 0, 0, 1, 0, 0];
this._path._map
.on('mousemove', this._onDrag, this)
.on('mouseup', this._onDragEnd, this)
this._path._dragMoved = false;
},
/**
* Dragging
* @param {L.MouseEvent} evt
*/
_onDrag: function(evt) {
var x = evt.containerPoint.x;
var y = evt.containerPoint.y;
var dx = x - this._startPoint.x;
var dy = y - this._startPoint.y;
if (!this._path._dragMoved && (dx || dy)) {
this._path._dragMoved = true;
this._path.fire('dragstart');
}
this._matrix[4] += dx;
this._matrix[5] += dy;
this._startPoint.x = x;
this._startPoint.y = y;
this._path._applyTransform(this._matrix);
this._path.fire('drag');
L.DomEvent.stop(evt.originalEvent);
},
/**
* Dragging stopped, apply
* @param {L.MouseEvent} evt
*/
_onDragEnd: function(evt) {
L.DomEvent.stop(evt);
// undo container transform
this._path._resetTransform();
// apply matrix
this._transformPoints(this._matrix);
this._path._map
.off('mousemove', this._onDrag, this)
.off('mouseup', this._onDragEnd, this);
// consistency
this._path.fire('dragend', {
distance: Math.sqrt(
L.LineUtil._sqDist(this._dragStartPoint, evt.containerPoint)
)
});
this._matrix = null;
this._startPoint = null;
this._dragStartPoint = null;
},
/**
* Applies transformation, does it in one sweep for performance,
* so don't be surprised about the code repetition.
*
* [ x ] [ a b tx ] [ x ] [ a * x + b * y + tx ]
* [ y ] = [ c d ty ] [ y ] = [ c * x + d * y + ty ]
*
* @param {Array.<Number>} matrix
*/
_transformPoints: function(matrix) {
var path = this._path;
var i, len, latlng;
var px = L.point(matrix[4], matrix[5]);
var crs = path._map.options.crs;
var transformation = crs.transformation;
var scale = crs.scale(path._map.getZoom());
var projection = crs.projection;
var diff = transformation.untransform(px, scale)
.subtract(transformation.untransform(L.point(0, 0), scale));
// console.time('transform');
// all shifts are in-place
if (path._point) { // L.Circle
path._latlng = projection.unproject(
projection.project(path._latlng)._add(diff));
path._point._add(px);
} else if (path._originalPoints) { // everything else
for (i = 0, len = path._originalPoints.length; i < len; i++) {
latlng = path._latlngs[i];
path._latlngs[i] = projection
.unproject(projection.project(latlng)._add(diff));
path._originalPoints[i]._add(px);
}
}
// holes operations
if (path._holes) {
for (i = 0, len = path._holes.length; i < len; i++) {
for (var j = 0, len2 = path._holes[i].length; j < len2; j++) {
latlng = path._holes[i][j];
path._holes[i][j] = projection
.unproject(projection.project(latlng)._add(diff));
path._holePoints[i][j]._add(px);
}
}
}
// console.timeEnd('transform');
path._updatePath();
}
});
L.Path.prototype.__initEvents = L.Path.prototype._initEvents;
L.Path.prototype._initEvents = function() {
this.__initEvents();
if (this.options.draggable) {
if (this.dragging) {
this.dragging.enable();
} else {
this.dragging = new L.Handler.PathDrag(this);
this.dragging.enable();
}
} else if (this.dragging) {
this.dragging.disable();
}
};
(function() {
// listen and propagate dragstart on sub-layers
L.FeatureGroup.EVENTS += ' dragstart';
function wrapMethod(klasses, methodName, method) {
for (var i = 0, len = klasses.length; i < len; i++) {
var klass = klasses[i];
klass.prototype['_' + methodName] = klass.prototype[methodName];
klass.prototype[methodName] = method;
}
}
/**
* @param {L.Polygon|L.Polyline} layer
* @return {L.MultiPolygon|L.MultiPolyline}
*/
function addLayer(layer) {
if (this.hasLayer(layer)) {
return this;
}
layer
.on('drag', this._onDrag, this)
.on('dragend', this._onDragEnd, this);
return this._addLayer.call(this, layer);
}
/**
* @param {L.Polygon|L.Polyline} layer
* @return {L.MultiPolygon|L.MultiPolyline}
*/
function removeLayer(layer) {
if (!this.hasLayer(layer)) {
return this;
}
layer
.off('drag', this._onDrag, this)
.off('dragend', this._onDragEnd, this);
return this._removeLayer.call(this, layer);
}
// duck-type methods to listen to the drag events
wrapMethod([L.MultiPolygon, L.MultiPolyline], 'addLayer', addLayer);
wrapMethod([L.MultiPolygon, L.MultiPolyline], 'removeLayer', removeLayer);
var dragMethods = {
_onDrag: function(evt) {
var layer = evt.target;
this.eachLayer(function(otherLayer) {
if (otherLayer !== layer) {
otherLayer._applyTransform(layer.dragging._matrix);
}
});
this._propagateEvent(evt);
},
_onDragEnd: function(evt) {
var layer = evt.target;
this.eachLayer(function(otherLayer) {
if (otherLayer !== layer) {
otherLayer._resetTransform();
otherLayer.dragging._transformPoints(layer.dragging._matrix);
}
});
this._propagateEvent(evt);
}
};
L.MultiPolygon.include(dragMethods);
L.MultiPolyline.include(dragMethods);
})();
// TODO: dismiss that on Leaflet 0.8.x release
L.Polygon.include( /** @lends L.Polygon.prototype */ {
/**
* @return {L.LatLng}
*/
getCenter: function() {
var i, j, len, p1, p2, f, area, x, y,
points = this._parts[0];
// polygon centroid algorithm; only uses the first ring if there are multiple
area = x = y = 0;
for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
p1 = points[i];
p2 = points[j];
f = p1.y * p2.x - p2.y * p1.x;
x += (p1.x + p2.x) * f;
y += (p1.y + p2.y) * f;
area += f * 3;
}
return this._map.layerPointToLatLng([x / area, y / area]);
}
});
"use strict";
/**
* Static flag for move markers
* @type {Boolean}
*/
L.EditToolbar.Edit.MOVE_MARKERS = false;
L.EditToolbar.Edit.include( /** @lends L.EditToolbar.Edit.prototype */ {
/**
* @override
*/
initialize: function(map, options) {
L.EditToolbar.Edit.MOVE_MARKERS = !!options.selectedPathOptions.moveMarkers;
this._initialize(map, options);
},
/**
* @param {L.Map} map
* @param {Object} options
*/
_initialize: L.EditToolbar.Edit.prototype.initialize
});
/**
* Mainly central marker routines
*/
L.Edit.SimpleShape.include( /** @lends L.Edit.SimpleShape.prototype */ {
/**
* Put move marker into center
*/
_updateMoveMarker: function() {
if (this._moveMarker) {
this._moveMarker.setLatLng(this._getShapeCenter());
}
},
/**
* Shape centroid
* @return {L.LatLng}
*/
_getShapeCenter: function() {
return this._shape.getBounds().getCenter();
},
/**
* @override
*/
_createMoveMarker: function() {
if (L.EditToolbar.Edit.MOVE_MARKERS) {
this._moveMarker = this._createMarker(this._getShapeCenter(),
this.options.moveIcon);
}
}
});
/**
* Override this if you don't want the central marker
* @type {Boolean}
*/
L.Edit.SimpleShape.mergeOptions({
moveMarker: false
});
/**
* Dragging routines for circle
*/
L.Edit.Circle.include( /** @lends L.Edit.Circle.prototype */ {
/**
* @override
*/
addHooks: function() {
if (this._shape._map) {
this._map = this._shape._map;
if (!this._markerGroup) {
this._enableDragging();
this._initMarkers();
}
this._shape._map.addLayer(this._markerGroup);
}
},
/**
* @override
*/
removeHooks: function() {
if (this._shape._map) {
for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
this._unbindMarker(this._resizeMarkers[i]);
}
this._disableDragging();
this._resizeMarkers = null;
this._map.removeLayer(this._markerGroup);
delete this._markerGroup;
}
this._map = null;
},
/**
* @override
*/
_createMoveMarker: L.Edit.SimpleShape.prototype._createMoveMarker,
/**
* Change
* @param {L.LatLng} latlng
*/
_resize: function(latlng) {
var center = this._shape.getLatLng();
var radius = center.distanceTo(latlng);
this._shape.setRadius(radius);
this._updateMoveMarker();
},
/**
* Adds drag start listeners
*/
_enableDragging: function() {
if (!this._shape.dragging) {
this._shape.dragging = new L.Handler.PathDrag(this._shape);
}
this._shape.dragging.enable();
this._shape
.on('dragstart', this._onStartDragFeature, this)
.on('dragend', this._onStopDragFeature, this);
},
/**
* Removes drag start listeners
*/
_disableDragging: function() {
this._shape.dragging.disable();
this._shape
.off('dragstart', this._onStartDragFeature, this)
.off('dragend', this._onStopDragFeature, this);
},
/**
* Start drag
* @param {L.MouseEvent} evt
*/
_onStartDragFeature: function() {
this._shape._map.removeLayer(this._markerGroup);
this._shape.fire('editstart');
},
/**
* Dragging stopped, apply
* @param {L.MouseEvent} evt
*/
_onStopDragFeature: function() {
var center = this._shape.getLatLng();
//this._moveMarker.setLatLng(center);
this._resizeMarkers[0].setLatLng(this._getResizeMarkerPoint(center));
// show resize marker
this._shape._map.addLayer(this._markerGroup);
this._updateMoveMarker();
this._fireEdit();
}
});
/**
* Dragging routines for poly handler
*/
L.Edit.Rectangle.include( /** @lends L.Edit.Rectangle.prototype */ {
/**
* @override
*/
addHooks: function() {
if (this._shape._map) {
if (!this._markerGroup) {
this._enableDragging();
this._initMarkers();
}
this._shape._map.addLayer(this._markerGroup);
}
},
/**
* @override
*/
removeHooks: function() {
if (this._shape._map) {
this._shape._map.removeLayer(this._markerGroup);
this._disableDragging();
delete this._markerGroup;
delete this._markers;
}
},
/**
* @override
*/
_resize: function(latlng) {
// Update the shape based on the current position of
// this corner and the opposite point
this._shape.setBounds(L.latLngBounds(latlng, this._oppositeCorner));
this._updateMoveMarker();
},
/**
* @override
*/
_onMarkerDragEnd: function(e) {
this._toggleCornerMarkers(1);
this._repositionCornerMarkers();
L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, e);
},
/**
* Adds drag start listeners
*/
_enableDragging: function() {
if (!this._shape.dragging) {
this._shape.dragging = new L.Handler.PathDrag(this._shape);
}
this._shape.dragging.enable();
this._shape
.on('dragstart', this._onStartDragFeature, this)
.on('dragend', this._onStopDragFeature, this);
},
/**
* Removes drag start listeners
*/
_disableDragging: function() {
this._shape.dragging.disable();
this._shape
.off('dragstart', this._onStartDragFeature, this)
.off('dragend', this._onStopDragFeature, this);
},
/**
* Start drag
* @param {L.MouseEvent} evt
*/
_onStartDragFeature: function() {
this._shape._map.removeLayer(this._markerGroup);
this._shape.fire('editstart');
},
/**
* Dragging stopped, apply
* @param {L.MouseEvent} evt
*/
_onStopDragFeature: function() {
var polygon = this._shape;
for (var i = 0, len = polygon._latlngs.length; i < len; i++) {
// update marker
var marker = this._resizeMarkers[i];
marker.setLatLng(polygon._latlngs[i]);
// this one's needed to update the path
marker._origLatLng = polygon._latlngs[i];
if (marker._middleLeft) {
marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
}
if (marker._middleRight) {
marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
}
}
// this._moveMarker.setLatLng(polygon.getBounds().getCenter());
// show vertices
this._shape._map.addLayer(this._markerGroup);
this._updateMoveMarker();
this._repositionCornerMarkers();
this._fireEdit();
}
});
/**
* Dragging routines for poly handler
*/
L.Edit.PolyVerticesEdit.include( /** @lends L.Edit.PolyVerticesEdit.prototype */ {
// store methods to call them in overrides
__createMarker: L.Edit.PolyVerticesEdit.prototype._createMarker,
__removeMarker: L.Edit.PolyVerticesEdit.prototype._removeMarker,
/**
* @override
*/
addHooks: function() {
if (this._poly._map) {
if (!this._markerGroup) {
this._enableDragging();
this._initMarkers();
// Create center marker
this._createMoveMarker();
}
this._poly._map.addLayer(this._markerGroup);
}
},
/**
* @override
*/
_createMoveMarker: function() {
if (L.EditToolbar.Edit.MOVE_MARKERS && (this._poly instanceof L.Polygon)) {
this._moveMarker = new L.Marker(this._getShapeCenter(), {
icon: this.options.moveIcon
});
this._moveMarker.on('mousedown', this._delegateToShape, this);
this._markerGroup.addLayer(this._moveMarker);
}
},
/**
* Start dragging through the marker
* @param {L.MouseEvent} evt
*/
_delegateToShape: function(evt) {
var poly = this._shape || this._poly;
var marker = evt.target;
poly.fire('mousedown', L.Util.extend(evt, {
containerPoint: L.DomUtil.getPosition(marker._icon)
.add(poly._map._getMapPanePos())
}));
},
/**
* Polygon centroid
* @return {L.LatLng}
*/
_getShapeCenter: function() {
return this._poly.getCenter();
},
/**
* @override
*/
removeHooks: function() {
if (this._poly._map) {
this._poly._map.removeLayer(this._markerGroup);
this._disableDragging();
delete this._markerGroup;
delete this._markers;
}
},
/**
* Adds drag start listeners
*/
_enableDragging: function() {
if (!this._poly.dragging) {
this._poly.dragging = new L.Handler.PathDrag(this._poly);
}
this._poly.dragging.enable();
this._poly
.on('dragstart', this._onStartDragFeature, this)
.on('dragend', this._onStopDragFeature, this);
},
/**
* Removes drag start listeners
*/
_disableDragging: function() {
this._poly.dragging.disable();
this._poly
.off('dragstart', this._onStartDragFeature, this)
.off('dragend', this._onStopDragFeature, this);
},
/**
* Start drag
* @param {L.MouseEvent} evt
*/
_onStartDragFeature: function(evt) {
this._poly._map.removeLayer(this._markerGroup);
this._poly.fire('editstart');
},
/**
* Dragging stopped, apply
* @param {L.MouseEvent} evt
*/
_onStopDragFeature: function(evt) {
// var polygon = this._poly;
for (var i = 0, len = this._latlngs.length; i < len; i++) {
// update marker
var marker = this._markers[i];
marker.setLatLng(this._latlngs[i]);
// this one's needed to update the path
marker._origLatLng = this._latlngs[i];
if (marker._middleLeft) {
marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
}
if (marker._middleRight) {
marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
}
}
// show vertices
this._poly._map.addLayer(this._markerGroup);
L.Edit.SimpleShape.prototype._updateMoveMarker.call(this);
this._fireEdit();
},
/**
* Copy from simple shape
*/
_updateMoveMarker: L.Edit.SimpleShape.prototype._updateMoveMarker,
/**
* @override
*/
_createMarker: function(latlng, index) {
var marker = this.__createMarker(latlng, index);
marker
.on('dragstart', this._hideMoveMarker, this)
.on('dragend', this._showUpdateMoveMarker, this);
return marker;
},
/**
* @override
*/
_removeMarker: function(marker) {
this.__removeMarker(marker);
marker
.off('dragstart', this._hideMoveMarker, this)
.off('dragend', this._showUpdateMoveMarker, this);
},
/**
* Hide move marker while dragging a vertex
*/
_hideMoveMarker: function() {
if (this._moveMarker) {
this._markerGroup.removeLayer(this._moveMarker);
}
},
/**
* Show and update move marker
*/
_showUpdateMoveMarker: function() {
if (this._moveMarker) {
this._markerGroup.addLayer(this._moveMarker);
this._updateMoveMarker();
}
}
});
/**
* @type {L.DivIcon}
*/
L.Edit.PolyVerticesEdit.prototype.options.moveIcon = new L.DivIcon({
iconSize: new L.Point(8, 8),
className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move'
});
/**
* Override this if you don't want the central marker
* @type {Boolean}
*/
L.Edit.PolyVerticesEdit.mergeOptions({
moveMarker: false
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,636 @@
/* required styles */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg,
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer {
max-width: none !important;
max-height: none !important;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
}
.leaflet-container.leaflet-touch-drag {
-ms-touch-action: pinch-zoom;
/* Fallback for FF which doesn't support pinch-zoom */
touch-action: none;
touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
-ms-touch-action: none;
touch-action: none;
}
.leaflet-container {
-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-pane { z-index: 400; }
.leaflet-tile-pane { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane { z-index: 500; }
.leaflet-marker-pane { z-index: 600; }
.leaflet-tooltip-pane { z-index: 650; }
.leaflet-popup-pane { z-index: 700; }
.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg { z-index: 200; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile {
will-change: opacity;
}
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-animated {
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
will-change: transform;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
-o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-interactive {
cursor: pointer;
}
.leaflet-grab {
cursor: -webkit-grab;
cursor: -moz-grab;
}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
cursor: crosshair;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
}
/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
pointer-events: none;
}
.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive {
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline: 0;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-container a.leaflet-active {
outline: 2px solid orange;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255,255,255,0.5);
}
/* general typography */
.leaflet-container {
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.leaflet-bar a,
.leaflet-bar a:hover {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.leaflet-touch .leaflet-bar a:last-child {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
font-size: 22px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* Default icon URLs */
.leaflet-default-icon-path {
background-image: url(images/marker-icon.png);
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.7);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover {
text-decoration: underline;
}
.leaflet-container .leaflet-control-attribution,
.leaflet-container .leaflet-control-scale {
font-size: 11px;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: #fff;
background: rgba(255, 255, 255, 0.5);
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
margin-bottom: 20px;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 19px;
line-height: 1.4;
}
.leaflet-popup-content p {
margin: 18px 0;
}
.leaflet-popup-tip-container {
width: 40px;
height: 20px;
position: absolute;
left: 50%;
margin-left: -20px;
overflow: hidden;
pointer-events: none;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
padding: 4px 4px 0 0;
border: none;
text-align: center;
width: 18px;
height: 14px;
font: 16px/14px Tahoma, Verdana, sans-serif;
color: #c3c3c3;
text-decoration: none;
font-weight: bold;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover {
color: #999;
}
.leaflet-popup-scrolled {
overflow: auto;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-popup-tip-container {
margin-top: -1px;
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
position: absolute;
padding: 6px;
background-color: #fff;
border: 1px solid #fff;
border-radius: 3px;
color: #222;
white-space: nowrap;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.leaflet-tooltip.leaflet-clickable {
cursor: pointer;
pointer-events: auto;
}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
position: absolute;
pointer-events: none;
border: 6px solid transparent;
background: transparent;
content: "";
}
/* Directions */
.leaflet-tooltip-bottom {
margin-top: 6px;
}
.leaflet-tooltip-top {
margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
left: 50%;
margin-left: -6px;
}
.leaflet-tooltip-top:before {
bottom: 0;
margin-bottom: -12px;
border-top-color: #fff;
}
.leaflet-tooltip-bottom:before {
top: 0;
margin-top: -12px;
margin-left: -6px;
border-bottom-color: #fff;
}
.leaflet-tooltip-left {
margin-left: -6px;
}
.leaflet-tooltip-right {
margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
top: 50%;
margin-top: -6px;
}
.leaflet-tooltip-left:before {
right: 0;
margin-right: -12px;
border-left-color: #fff;
}
.leaflet-tooltip-right:before {
left: 0;
margin-left: -12px;
border-right-color: #fff;
}

View File

@@ -0,0 +1,682 @@
// Packaging/modules magic dance.
(function (factory) {
var L;
if (typeof define === 'function' && define.amd) {
// AMD
define(['leaflet'], factory);
} else if (typeof module !== 'undefined') {
// Node/CommonJS
L = require('leaflet');
module.exports = factory(L);
} else {
// Browser globals
if (typeof window.L === 'undefined')
throw 'Leaflet must be loaded first';
factory(window.L);
}
}(function (L) {
"use strict";
/**
* @fileOverview Leaflet Geometry utilities for distances and linear referencing.
* @name L.GeometryUtil
*/
L.GeometryUtil = L.extend(L.GeometryUtil || {}, {
/**
Shortcut function for planar distance between two {L.LatLng} at current zoom.
@tutorial distance-length
@param {L.Map} map Leaflet map to be used for this method
@param {L.LatLng} latlngA geographical point A
@param {L.LatLng} latlngB geographical point B
@returns {Number} planar distance
*/
distance: function (map, latlngA, latlngB) {
return map.latLngToLayerPoint(latlngA).distanceTo(map.latLngToLayerPoint(latlngB));
},
/**
Shortcut function for planar distance between a {L.LatLng} and a segment (A-B).
@param {L.Map} map Leaflet map to be used for this method
@param {L.LatLng} latlng - The position to search
@param {L.LatLng} latlngA geographical point A of the segment
@param {L.LatLng} latlngB geographical point B of the segment
@returns {Number} planar distance
*/
distanceSegment: function (map, latlng, latlngA, latlngB) {
var p = map.latLngToLayerPoint(latlng),
p1 = map.latLngToLayerPoint(latlngA),
p2 = map.latLngToLayerPoint(latlngB);
return L.LineUtil.pointToSegmentDistance(p, p1, p2);
},
/**
Shortcut function for converting distance to readable distance.
@param {Number} distance distance to be converted
@param {String} unit 'metric' or 'imperial'
@returns {String} in yard or miles
*/
readableDistance: function (distance, unit) {
var isMetric = (unit !== 'imperial'),
distanceStr;
if (isMetric) {
// show metres when distance is < 1km, then show km
if (distance > 1000) {
distanceStr = (distance / 1000).toFixed(2) + ' km';
}
else {
distanceStr = Math.ceil(distance) + ' m';
}
}
else {
distance *= 1.09361;
if (distance > 1760) {
distanceStr = (distance / 1760).toFixed(2) + ' miles';
}
else {
distanceStr = Math.ceil(distance) + ' yd';
}
}
return distanceStr;
},
/**
Returns true if the latlng belongs to segment A-B
@param {L.LatLng} latlng - The position to search
@param {L.LatLng} latlngA geographical point A of the segment
@param {L.LatLng} latlngB geographical point B of the segment
@param {?Number} [tolerance=0.2] tolerance to accept if latlng belongs really
@returns {boolean}
*/
belongsSegment: function(latlng, latlngA, latlngB, tolerance) {
tolerance = tolerance === undefined ? 0.2 : tolerance;
var hypotenuse = latlngA.distanceTo(latlngB),
delta = latlngA.distanceTo(latlng) + latlng.distanceTo(latlngB) - hypotenuse;
return delta/hypotenuse < tolerance;
},
/**
* Returns total length of line
* @tutorial distance-length
*
* @param {L.Polyline|Array<L.Point>|Array<L.LatLng>} coords Set of coordinates
* @returns {Number} Total length (pixels for Point, meters for LatLng)
*/
length: function (coords) {
var accumulated = L.GeometryUtil.accumulatedLengths(coords);
return accumulated.length > 0 ? accumulated[accumulated.length-1] : 0;
},
/**
* Returns a list of accumulated length along a line.
* @param {L.Polyline|Array<L.Point>|Array<L.LatLng>} coords Set of coordinates
* @returns {Array<Number>} Array of accumulated lengths (pixels for Point, meters for LatLng)
*/
accumulatedLengths: function (coords) {
if (typeof coords.getLatLngs == 'function') {
coords = coords.getLatLngs();
}
if (coords.length === 0)
return [];
var total = 0,
lengths = [0];
for (var i = 0, n = coords.length - 1; i< n; i++) {
total += coords[i].distanceTo(coords[i+1]);
lengths.push(total);
}
return lengths;
},
/**
Returns the closest point of a {L.LatLng} on the segment (A-B)
@tutorial closest
@param {L.Map} map Leaflet map to be used for this method
@param {L.LatLng} latlng - The position to search
@param {L.LatLng} latlngA geographical point A of the segment
@param {L.LatLng} latlngB geographical point B of the segment
@returns {L.LatLng} Closest geographical point
*/
closestOnSegment: function (map, latlng, latlngA, latlngB) {
var maxzoom = map.getMaxZoom();
if (maxzoom === Infinity)
maxzoom = map.getZoom();
var p = map.project(latlng, maxzoom),
p1 = map.project(latlngA, maxzoom),
p2 = map.project(latlngB, maxzoom),
closest = L.LineUtil.closestPointOnSegment(p, p1, p2);
return map.unproject(closest, maxzoom);
},
/**
Returns the closest latlng on layer.
Accept nested arrays
@tutorial closest
@param {L.Map} map Leaflet map to be used for this method
@param {Array<L.LatLng>|Array<Array<L.LatLng>>|L.PolyLine|L.Polygon} layer - Layer that contains the result
@param {L.LatLng} latlng - The position to search
@param {?boolean} [vertices=false] - Whether to restrict to path vertices.
@returns {L.LatLng} Closest geographical point or null if layer param is incorrect
*/
closest: function (map, layer, latlng, vertices) {
var latlngs,
mindist = Infinity,
result = null,
i, n, distance;
if (layer instanceof Array) {
// if layer is Array<Array<T>>
if (layer[0] instanceof Array && typeof layer[0][0] !== 'number') {
// if we have nested arrays, we calc the closest for each array
// recursive
for (var i = 0; i < layer.length; i++) {
var subResult = L.GeometryUtil.closest(map, layer[i], latlng, vertices);
if (subResult.distance < mindist) {
mindist = subResult.distance;
result = subResult;
}
}
return result;
} else if (layer[0] instanceof L.LatLng || typeof layer[0][0] === 'number') { // we could have a latlng as [x,y] with x & y numbers
layer = L.polyline(layer);
} else {
return result;
}
}
// if we don't have here a Polyline, that means layer is incorrect
// see https://github.com/makinacorpus/Leaflet.GeometryUtil/issues/23
if (! ( layer instanceof L.Polyline ) )
return result;
/**
* Flat an array upon a predicate, saying if we have to dig digger or not
* Specific use for Polygon, Polyline, MultiPolyline (0.7.7)
* Leaflet 1.0 change the structure of Polyline (could be MultiPolyline > nested arrays), idem for Polygon
*/
function flattenArray(array, predicate, result) {
var index = -1,
length = array.length;
predicate || ( predicate = function(v) { return true } );
result || ( result = []);
if (predicate(array)) {
while (++index < length) {
var value = array[index];
if (predicate(value)) {
flattenArray(value, predicate, result);
} else {
result.push(value);
}
}
} else {
result.push(array);
}
return result;
}
latlngs = flattenArray(layer.getLatLngs().slice(0), function isFlattenable(value) {
return ( ( value instanceof Array && typeof value[0] !== 'number' ) && ! ( value instanceof L.LatLng ) )
});
// Lookup vertices
if (vertices) {
for(i = 0, n = latlngs.length; i < n; i++) {
var ll = latlngs[i];
distance = L.GeometryUtil.distance(map, latlng, ll);
if (distance < mindist) {
mindist = distance;
result = ll;
result.distance = distance;
}
}
return result;
}
// add the first point to close the polygon
if (layer instanceof L.Polygon) {
latlngs.push(latlngs[0]);
}
// Keep the closest point of all segments
for (i = 0, n = latlngs.length; i < n-1; i++) {
var latlngA = latlngs[i],
latlngB = latlngs[i+1];
distance = L.GeometryUtil.distanceSegment(map, latlng, latlngA, latlngB);
if (distance <= mindist) {
mindist = distance;
result = L.GeometryUtil.closestOnSegment(map, latlng, latlngA, latlngB);
result.distance = distance;
}
}
return result;
},
/**
Returns the closest layer to latlng among a list of layers.
@tutorial closest
@param {L.Map} map Leaflet map to be used for this method
@param {Array<L.ILayer>} layers Set of layers
@param {L.LatLng} latlng - The position to search
@returns {object} ``{layer, latlng, distance}`` or ``null`` if list is empty;
*/
closestLayer: function (map, layers, latlng) {
var mindist = Infinity,
result = null,
ll = null,
distance = Infinity;
for (var i = 0, n = layers.length; i < n; i++) {
var layer = layers[i];
if (layer instanceof L.LayerGroup) {
// recursive
var subResult = L.GeometryUtil.closestLayer(map, layer.getLayers(), latlng);
if (subResult.distance < mindist) {
mindist = subResult.distance;
result = subResult;
}
} else {
// Single dimension, snap on points, else snap on closest
if (typeof layer.getLatLng == 'function') {
ll = layer.getLatLng();
distance = L.GeometryUtil.distance(map, latlng, ll);
}
else {
ll = L.GeometryUtil.closest(map, layer, latlng);
if (ll) distance = ll.distance; // Can return null if layer has no points.
}
if (distance < mindist) {
mindist = distance;
result = {layer: layer, latlng: ll, distance: distance};
}
}
}
return result;
},
/**
* Returns all layers within a radius of the given position, in an ascending order of distance.
@param {L.Map} map Leaflet map to be used for this method
@param {Array<ILayer>} layers - A list of layers.
@param {L.LatLng} latlng - The position to search
@param {?Number} [radius=Infinity] - Search radius in pixels
@return {object[]} an array of objects including layer within the radius, closest latlng, and distance
*/
layersWithin: function(map, layers, latlng, radius) {
radius = typeof radius == 'number' ? radius : Infinity;
var results = [];
var ll = null;
var distance = 0;
for (var i = 0, n = layers.length; i < n; i++) {
var layer = layers[i];
if (typeof layer.getLatLng == 'function') {
ll = layer.getLatLng();
distance = L.GeometryUtil.distance(map, latlng, ll);
}
else {
ll = L.GeometryUtil.closest(map, layer, latlng);
if (ll) distance = ll.distance; // Can return null if layer has no points.
}
if (ll && distance < radius) {
results.push({layer: layer, latlng: ll, distance: distance});
}
}
var sortedResults = results.sort(function(a, b) {
return a.distance - b.distance;
});
return sortedResults;
},
/**
Returns the closest position from specified {LatLng} among specified layers,
with a maximum tolerance in pixels, providing snapping behaviour.
@tutorial closest
@param {L.Map} map Leaflet map to be used for this method
@param {Array<ILayer>} layers - A list of layers to snap on.
@param {L.LatLng} latlng - The position to snap
@param {?Number} [tolerance=Infinity] - Maximum number of pixels.
@param {?boolean} [withVertices=true] - Snap to layers vertices or segment points (not only vertex)
@returns {object} with snapped {LatLng} and snapped {Layer} or null if tolerance exceeded.
*/
closestLayerSnap: function (map, layers, latlng, tolerance, withVertices) {
tolerance = typeof tolerance == 'number' ? tolerance : Infinity;
withVertices = typeof withVertices == 'boolean' ? withVertices : true;
var result = L.GeometryUtil.closestLayer(map, layers, latlng);
if (!result || result.distance > tolerance)
return null;
// If snapped layer is linear, try to snap on vertices (extremities and middle points)
if (withVertices && typeof result.layer.getLatLngs == 'function') {
var closest = L.GeometryUtil.closest(map, result.layer, result.latlng, true);
if (closest.distance < tolerance) {
result.latlng = closest;
result.distance = L.GeometryUtil.distance(map, closest, latlng);
}
}
return result;
},
/**
Returns the Point located on a segment at the specified ratio of the segment length.
@param {L.Point} pA coordinates of point A
@param {L.Point} pB coordinates of point B
@param {Number} the length ratio, expressed as a decimal between 0 and 1, inclusive.
@returns {L.Point} the interpolated point.
*/
interpolateOnPointSegment: function (pA, pB, ratio) {
return L.point(
(pA.x * (1 - ratio)) + (ratio * pB.x),
(pA.y * (1 - ratio)) + (ratio * pB.y)
);
},
/**
Returns the coordinate of the point located on a line at the specified ratio of the line length.
@param {L.Map} map Leaflet map to be used for this method
@param {Array<L.LatLng>|L.PolyLine} latlngs Set of geographical points
@param {Number} ratio the length ratio, expressed as a decimal between 0 and 1, inclusive
@returns {Object} an object with latLng ({LatLng}) and predecessor ({Number}), the index of the preceding vertex in the Polyline
(-1 if the interpolated point is the first vertex)
*/
interpolateOnLine: function (map, latLngs, ratio) {
latLngs = (latLngs instanceof L.Polyline) ? latLngs.getLatLngs() : latLngs;
var n = latLngs.length;
if (n < 2) {
return null;
}
// ensure the ratio is between 0 and 1;
ratio = Math.max(Math.min(ratio, 1), 0);
if (ratio === 0) {
return {
latLng: latLngs[0] instanceof L.LatLng ? latLngs[0] : L.latLng(latLngs[0]),
predecessor: -1
};
}
if (ratio == 1) {
return {
latLng: latLngs[latLngs.length -1] instanceof L.LatLng ? latLngs[latLngs.length -1] : L.latLng(latLngs[latLngs.length -1]),
predecessor: latLngs.length - 2
};
}
// project the LatLngs as Points,
// and compute total planar length of the line at max precision
var maxzoom = map.getMaxZoom();
if (maxzoom === Infinity)
maxzoom = map.getZoom();
var pts = [];
var lineLength = 0;
for(var i = 0; i < n; i++) {
pts[i] = map.project(latLngs[i], maxzoom);
if(i > 0)
lineLength += pts[i-1].distanceTo(pts[i]);
}
var ratioDist = lineLength * ratio;
var a = pts[0],
b = pts[1],
distA = 0,
distB = a.distanceTo(b);
// follow the line segments [ab], adding lengths,
// until we find the segment where the points should lie on
var index = 1;
for (; index < n && distB < ratioDist; index++) {
a = b;
distA = distB;
b = pts[index];
distB += a.distanceTo(b);
}
// compute the ratio relative to the segment [ab]
var segmentRatio = ((distB - distA) !== 0) ? ((ratioDist - distA) / (distB - distA)) : 0;
var interpolatedPoint = L.GeometryUtil.interpolateOnPointSegment(a, b, segmentRatio);
return {
latLng: map.unproject(interpolatedPoint, maxzoom),
predecessor: index-2
};
},
/**
Returns a float between 0 and 1 representing the location of the
closest point on polyline to the given latlng, as a fraction of total line length.
(opposite of L.GeometryUtil.interpolateOnLine())
@param {L.Map} map Leaflet map to be used for this method
@param {L.PolyLine} polyline Polyline on which the latlng will be search
@param {L.LatLng} latlng The position to search
@returns {Number} Float between 0 and 1
*/
locateOnLine: function (map, polyline, latlng) {
var latlngs = polyline.getLatLngs();
if (latlng.equals(latlngs[0]))
return 0.0;
if (latlng.equals(latlngs[latlngs.length-1]))
return 1.0;
var point = L.GeometryUtil.closest(map, polyline, latlng, false),
lengths = L.GeometryUtil.accumulatedLengths(latlngs),
total_length = lengths[lengths.length-1],
portion = 0,
found = false;
for (var i=0, n = latlngs.length-1; i < n; i++) {
var l1 = latlngs[i],
l2 = latlngs[i+1];
portion = lengths[i];
if (L.GeometryUtil.belongsSegment(point, l1, l2)) {
portion += l1.distanceTo(point);
found = true;
break;
}
}
if (!found) {
throw "Could not interpolate " + latlng.toString() + " within " + polyline.toString();
}
return portion / total_length;
},
/**
Returns a clone with reversed coordinates.
@param {L.PolyLine} polyline polyline to reverse
@returns {L.PolyLine} polyline reversed
*/
reverse: function (polyline) {
return L.polyline(polyline.getLatLngs().slice(0).reverse());
},
/**
Returns a sub-part of the polyline, from start to end.
If start is superior to end, returns extraction from inverted line.
@param {L.Map} map Leaflet map to be used for this method
@param {L.PolyLine} polyline Polyline on which will be extracted the sub-part
@param {Number} start ratio, expressed as a decimal between 0 and 1, inclusive
@param {Number} end ratio, expressed as a decimal between 0 and 1, inclusive
@returns {Array<L.LatLng>} new polyline
*/
extract: function (map, polyline, start, end) {
if (start > end) {
return L.GeometryUtil.extract(map, L.GeometryUtil.reverse(polyline), 1.0-start, 1.0-end);
}
// Bound start and end to [0-1]
start = Math.max(Math.min(start, 1), 0);
end = Math.max(Math.min(end, 1), 0);
var latlngs = polyline.getLatLngs(),
startpoint = L.GeometryUtil.interpolateOnLine(map, polyline, start),
endpoint = L.GeometryUtil.interpolateOnLine(map, polyline, end);
// Return single point if start == end
if (start == end) {
var point = L.GeometryUtil.interpolateOnLine(map, polyline, end);
return [point.latLng];
}
// Array.slice() works indexes at 0
if (startpoint.predecessor == -1)
startpoint.predecessor = 0;
if (endpoint.predecessor == -1)
endpoint.predecessor = 0;
var result = latlngs.slice(startpoint.predecessor+1, endpoint.predecessor+1);
result.unshift(startpoint.latLng);
result.push(endpoint.latLng);
return result;
},
/**
Returns true if first polyline ends where other second starts.
@param {L.PolyLine} polyline First polyline
@param {L.PolyLine} other Second polyline
@returns {bool}
*/
isBefore: function (polyline, other) {
if (!other) return false;
var lla = polyline.getLatLngs(),
llb = other.getLatLngs();
return (lla[lla.length-1]).equals(llb[0]);
},
/**
Returns true if first polyline starts where second ends.
@param {L.PolyLine} polyline First polyline
@param {L.PolyLine} other Second polyline
@returns {bool}
*/
isAfter: function (polyline, other) {
if (!other) return false;
var lla = polyline.getLatLngs(),
llb = other.getLatLngs();
return (lla[0]).equals(llb[llb.length-1]);
},
/**
Returns true if first polyline starts where second ends or start.
@param {L.PolyLine} polyline First polyline
@param {L.PolyLine} other Second polyline
@returns {bool}
*/
startsAtExtremity: function (polyline, other) {
if (!other) return false;
var lla = polyline.getLatLngs(),
llb = other.getLatLngs(),
start = lla[0];
return start.equals(llb[0]) || start.equals(llb[llb.length-1]);
},
/**
Returns horizontal angle in degres between two points.
@param {L.Point} a Coordinates of point A
@param {L.Point} b Coordinates of point B
@returns {Number} horizontal angle
*/
computeAngle: function(a, b) {
return (Math.atan2(b.y - a.y, b.x - a.x) * 180 / Math.PI);
},
/**
Returns slope (Ax+B) between two points.
@param {L.Point} a Coordinates of point A
@param {L.Point} b Coordinates of point B
@returns {Object} with ``a`` and ``b`` properties.
*/
computeSlope: function(a, b) {
var s = (b.y - a.y) / (b.x - a.x),
o = a.y - (s * a.x);
return {'a': s, 'b': o};
},
/**
Returns LatLng of rotated point around specified LatLng center.
@param {L.LatLng} latlngPoint: point to rotate
@param {double} angleDeg: angle to rotate in degrees
@param {L.LatLng} latlngCenter: center of rotation
@returns {L.LatLng} rotated point
*/
rotatePoint: function(map, latlngPoint, angleDeg, latlngCenter) {
var maxzoom = map.getMaxZoom();
if (maxzoom === Infinity)
maxzoom = map.getZoom();
var angleRad = angleDeg*Math.PI/180,
pPoint = map.project(latlngPoint, maxzoom),
pCenter = map.project(latlngCenter, maxzoom),
x2 = Math.cos(angleRad)*(pPoint.x-pCenter.x) - Math.sin(angleRad)*(pPoint.y-pCenter.y) + pCenter.x,
y2 = Math.sin(angleRad)*(pPoint.x-pCenter.x) + Math.cos(angleRad)*(pPoint.y-pCenter.y) + pCenter.y;
return map.unproject(new L.Point(x2,y2), maxzoom);
},
/**
Returns the bearing in degrees clockwise from north (0 degrees)
from the first L.LatLng to the second, at the first LatLng
@param {L.LatLng} latlng1: origin point of the bearing
@param {L.LatLng} latlng2: destination point of the bearing
@returns {float} degrees clockwise from north.
*/
bearing: function(latlng1, latlng2) {
var rad = Math.PI / 180,
lat1 = latlng1.lat * rad,
lat2 = latlng2.lat * rad,
lon1 = latlng1.lng * rad,
lon2 = latlng2.lng * rad,
y = Math.sin(lon2 - lon1) * Math.cos(lat2),
x = Math.cos(lat1) * Math.sin(lat2) -
Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);
var bearing = ((Math.atan2(y, x) * 180 / Math.PI) + 360) % 360;
return bearing >= 180 ? bearing-360 : bearing;
},
/**
Returns the point that is a distance and heading away from
the given origin point.
@param {L.LatLng} latlng: origin point
@param {float}: heading in degrees, clockwise from 0 degrees north.
@param {float}: distance in meters
@returns {L.latLng} the destination point.
Many thanks to Chris Veness at http://www.movable-type.co.uk/scripts/latlong.html
for a great reference and examples.
*/
destination: function(latlng, heading, distance) {
heading = (heading + 360) % 360;
var rad = Math.PI / 180,
radInv = 180 / Math.PI,
R = 6378137, // approximation of Earth's radius
lon1 = latlng.lng * rad,
lat1 = latlng.lat * rad,
rheading = heading * rad,
sinLat1 = Math.sin(lat1),
cosLat1 = Math.cos(lat1),
cosDistR = Math.cos(distance / R),
sinDistR = Math.sin(distance / R),
lat2 = Math.asin(sinLat1 * cosDistR + cosLat1 *
sinDistR * Math.cos(rheading)),
lon2 = lon1 + Math.atan2(Math.sin(rheading) * sinDistR *
cosLat1, cosDistR - sinLat1 * Math.sin(lat2));
lon2 = lon2 * radInv;
lon2 = lon2 > 180 ? lon2 - 360 : lon2 < -180 ? lon2 + 360 : lon2;
return L.latLng([lat2 * radInv, lon2]);
}
});
return L.GeometryUtil;
}));

View File

@@ -0,0 +1,360 @@
(function () {
L.Handler.MarkerSnap = L.Handler.extend({
options: {
snapDistance: 15, // in pixels
snapVertices: true
},
initialize: function (map, marker, options) {
L.Handler.prototype.initialize.call(this, map);
this._markers = [];
this._guides = [];
if (arguments.length == 2) {
if (!(marker instanceof L.Class)) {
options = marker;
marker = null;
}
}
L.Util.setOptions(this, options || {});
if (marker) {
// new markers should be draggable !
if (!marker.dragging) marker.dragging = new L.Handler.MarkerDrag(marker);
marker.dragging.enable();
this.watchMarker(marker);
}
// Convert snap distance in pixels into buffer in degres, for searching around mouse
// It changes at each zoom change.
function computeBuffer() {
this._buffer = map.layerPointToLatLng(new L.Point(0,0)).lat -
map.layerPointToLatLng(new L.Point(this.options.snapDistance, 0)).lat;
}
map.on('zoomend', computeBuffer, this);
map.whenReady(computeBuffer, this);
computeBuffer.call(this);
},
enable: function () {
this.disable();
for (var i=0; i<this._markers.length; i++) {
this.watchMarker(this._markers[i]);
}
},
disable: function () {
for (var i=0; i<this._markers.length; i++) {
this.unwatchMarker(this._markers[i]);
}
},
watchMarker: function (marker) {
if (this._markers.indexOf(marker) == -1)
this._markers.push(marker);
marker.on('move', this._snapMarker, this);
},
unwatchMarker: function (marker) {
marker.off('move', this._snapMarker, this);
delete marker['snap'];
},
addGuideLayer: function (layer) {
for (var i=0, n=this._guides.length; i<n; i++)
if (L.stamp(layer) === L.stamp(this._guides[i]))
return;
this._guides.push(layer);
},
_snapMarker: function(e) {
var marker = e.target,
latlng = marker.getLatLng(),
snaplist = [];
function isDifferentLayer(layer) {
if (layer.getLatLng) {
return L.stamp(marker) !== L.stamp(layer);
} else {
if (layer.editing && layer.editing._enabled) {
var points = layer.editing._verticesHandlers[0]._markerGroup.getLayers();
for(var i = 0, n = points.length; i < n; i++) {
if (L.stamp(points[i]) === L.stamp(marker)) { return false; }
}
}
}
return true;
}
function processGuide(guide) {
if ((guide._layers !== undefined) &&
(typeof guide.searchBuffer !== 'function')) {
// Guide is a layer group and has no L.LayerIndexMixin (from Leaflet.LayerIndex)
for (var id in guide._layers) {
processGuide(guide._layers[id]);
}
}
else if (typeof guide.searchBuffer === 'function') {
// Search snaplist around mouse
var nearlayers = guide.searchBuffer(latlng, this._buffer);
snaplist = snaplist.concat(nearlayers.filter(function(layer) {
return isDifferentLayer(layer);
}));
}
// Make sure the marker doesn't snap to itself or the associated polyline layer
else if (isDifferentLayer(guide)) {
snaplist.push(guide);
}
}
for (var i=0, n = this._guides.length; i < n; i++) {
var guide = this._guides[i];
processGuide.call(this, guide);
}
var closest = this._findClosestLayerSnap(this._map,
snaplist,
latlng,
this.options.snapDistance,
this.options.snapVertices);
closest = closest || {layer: null, latlng: null};
this._updateSnap(marker, closest.layer, closest.latlng);
},
_findClosestLayerSnap: function (map, layers, latlng, tolerance, withVertices) {
return L.GeometryUtil.closestLayerSnap(map, layers, latlng, tolerance, withVertices);
},
_updateSnap: function (marker, layer, latlng) {
if (layer && latlng) {
marker._latlng = L.latLng(latlng);
marker.update();
if (marker.snap != layer) {
marker.snap = layer;
if (marker._icon) L.DomUtil.addClass(marker._icon, 'marker-snapped');
marker.fire('snap', {layer:layer, latlng: latlng});
}
}
else {
if (marker.snap) {
if (marker._icon) L.DomUtil.removeClass(marker._icon, 'marker-snapped');
marker.fire('unsnap', {layer:marker.snap});
}
delete marker['snap'];
}
}
});
if (!L.Edit) {
// Leaflet.Draw not available.
return;
}
L.Handler.PolylineSnap = L.Edit.Poly.extend({
initialize: function (map, poly, options) {
var that = this;
L.Edit.Poly.prototype.initialize.call(this, poly, options);
this._snapper = new L.Handler.MarkerSnap(map, options);
poly.on('remove', function() {
that.disable();
});
},
addGuideLayer: function (layer) {
this._snapper.addGuideLayer(layer);
},
_initHandlers: function () {
this._verticesHandlers = [];
for (var i = 0; i < this.latlngs.length; i++) {
this._verticesHandlers.push(new L.Edit.PolyVerticesEditSnap(this._poly, this.latlngs[i], this.options));
}
}
});
L.Edit.PolyVerticesEditSnap = L.Edit.PolyVerticesEdit.extend({
_createMarker: function (latlng, index) {
var marker = L.Edit.PolyVerticesEdit.prototype._createMarker.call(this, latlng, index);
// Treat middle markers differently
var isMiddle = index === undefined;
if (isMiddle) {
// Snap middle markers, only once they were touched
marker.on('dragstart', function () {
this._poly.snapediting._snapper.watchMarker(marker);
}, this);
}
else {
this._poly.snapediting._snapper.watchMarker(marker);
}
return marker;
}
});
L.EditToolbar.SnapEdit = L.EditToolbar.Edit.extend({
snapOptions: {
snapDistance: 15, // in pixels
snapVertices: true
},
initialize: function(map, options) {
L.EditToolbar.Edit.prototype.initialize.call(this, map, options);
if (options.snapOptions) {
L.Util.extend(this.snapOptions, options.snapOptions);
}
if (Array.isArray(this.snapOptions.guideLayers)) {
this._guideLayers = this.snapOptions.guideLayers;
} else if (options.guideLayers instanceof L.LayerGroup) {
this._guideLayers = this.snapOptions.guideLayers.getLayers();
} else {
this._guideLayers = [];
}
},
addGuideLayer: function(layer) {
var index = this._guideLayers.findIndex(function(guideLayer) {
return L.stamp(layer) === L.stamp(guideLayer);
});
if (index === -1) {
this._guideLayers.push(layer);
this._featureGroup.eachLayer(function(layer) {
if (layer.snapediting) { layer.snapediting._guides.push(layer); }
});
}
},
removeGuideLayer: function(layer) {
var index = this._guideLayers.findIndex(function(guideLayer) {
return L.stamp(layer) === L.stamp(guideLayer);
});
if (index !== -1) {
this._guideLayers.splice(index, 1);
this._featureGroup.eachLayer(function(layer) {
if (layer.snapediting) { layer.snapediting._guides.splice(index, 1); }
});
}
},
clearGuideLayers: function() {
this._guideLayers = [];
this._featureGroup.eachLayer(function(layer) {
if (layer.snapediting) { layer.snapediting._guides = []; }
});
},
_enableLayerEdit: function(e) {
L.EditToolbar.Edit.prototype._enableLayerEdit.call(this, e);
var layer = e.layer || e.target || e;
if (!layer.snapediting) {
if (layer.getLatLng) {
layer.snapediting = new L.Handler.MarkerSnap(layer._map, layer, this.snapOptions);
} else {
if (layer.editing) {
layer.editing._verticesHandlers[0]._markerGroup.clearLayers();
delete layer.editing;
}
layer.editing = layer.snapediting = new L.Handler.PolylineSnap(layer._map, layer, this.snapOptions);
}
for (var i = 0, n = this._guideLayers.length; i < n; i++) {
layer.snapediting.addGuideLayer(this._guideLayers[i]);
}
}
layer.snapediting.enable();
}
});
L.Draw.Feature.SnapMixin = {
_snap_initialize: function () {
this.on('enabled', this._snap_on_enabled, this);
this.on('disabled', this._snap_on_disabled, this);
},
_snap_on_enabled: function () {
if (!this.options.guideLayers) {
return;
}
if (!this._mouseMarker) {
this._map.on('layeradd', this._snap_on_enabled, this);
return;
}else{
this._map.off('layeradd', this._snap_on_enabled, this);
}
if (!this._snapper) {
this._snapper = new L.Handler.MarkerSnap(this._map);
if (this.options.snapDistance) {
this._snapper.options.snapDistance = this.options.snapDistance;
}
if (this.options.snapVertices) {
this._snapper.options.snapVertices = this.options.snapVertices;
}
}
for (var i=0, n=this.options.guideLayers.length; i<n; i++)
this._snapper.addGuideLayer(this.options.guideLayers[i]);
var marker = this._mouseMarker;
this._snapper.watchMarker(marker);
// Show marker when (snap for user feedback)
var icon = marker.options.icon;
marker.on('snap', function (e) {
marker.setIcon(this.options.icon);
marker.setOpacity(1);
}, this)
.on('unsnap', function (e) {
marker.setIcon(icon);
marker.setOpacity(0);
}, this);
marker.on('click', this._snap_on_click, this);
},
_snap_on_click: function (e) {
if (this._markers) {
var markerCount = this._markers.length,
marker = this._markers[markerCount - 1];
if (this._mouseMarker.snap) {
if(e){
// update the feature being drawn to reflect the snapped location:
marker.setLatLng(e.target._latlng);
if(this._poly){
var polyPointsCount = this._poly._latlngs.length;
this._poly._latlngs[polyPointsCount - 1] = e.target._latlng;
this._poly.redraw();
}
}
L.DomUtil.addClass(marker._icon, 'marker-snapped');
}
}
},
_snap_on_disabled: function () {
delete this._snapper;
},
};
L.Draw.Feature.include(L.Draw.Feature.SnapMixin);
L.Draw.Feature.addInitHook('_snap_initialize');
})();

View File

@@ -0,0 +1,519 @@
/***
Spectrum Colorpicker v1.3.4
https://github.com/bgrins/spectrum
Author: Brian Grinstead
License: MIT
***/
.sp-container {
position:absolute;
top:0;
left:0;
display:inline-block;
*display: inline;
*zoom: 1;
/* https://github.com/bgrins/spectrum/issues/40 */
z-index: 9999994;
overflow: hidden;
}
.sp-container.sp-flat {
position: relative;
}
/* Fix for * { box-sizing: border-box; } */
.sp-container,
.sp-container * {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
/* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */
.sp-top {
position:relative;
width: 100%;
display:inline-block;
}
.sp-top-inner {
position:absolute;
top:0;
left:0;
bottom:0;
right:0;
}
.sp-color {
position: absolute;
top:0;
left:0;
bottom:0;
right:20%;
}
.sp-hue {
position: absolute;
top:0;
right:0;
bottom:0;
left:84%;
height: 100%;
}
.sp-clear-enabled .sp-hue {
top:33px;
height: 77.5%;
}
.sp-fill {
padding-top: 80%;
}
.sp-sat, .sp-val {
position: absolute;
top:0;
left:0;
right:0;
bottom:0;
}
.sp-alpha-enabled .sp-top {
margin-bottom: 18px;
}
.sp-alpha-enabled .sp-alpha {
display: block;
}
.sp-alpha-handle {
position:absolute;
top:-4px;
bottom: -4px;
width: 6px;
left: 50%;
cursor: pointer;
border: 1px solid black;
background: white;
opacity: .8;
}
.sp-alpha {
display: none;
position: absolute;
bottom: -14px;
right: 0;
left: 0;
height: 8px;
}
.sp-alpha-inner {
border: solid 1px #333;
}
.sp-clear {
display: none;
}
.sp-clear.sp-clear-display {
background-position: center;
}
.sp-clear-enabled .sp-clear {
display: block;
position:absolute;
top:0px;
right:0;
bottom:0;
left:84%;
height: 28px;
}
/* Don't allow text selection */
.sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-clear, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button {
-webkit-user-select:none;
-moz-user-select: -moz-none;
-o-user-select:none;
user-select: none;
}
.sp-container.sp-input-disabled .sp-input-container {
display: none;
}
.sp-container.sp-buttons-disabled .sp-button-container {
display: none;
}
.sp-palette-only .sp-picker-container {
display: none;
}
.sp-palette-disabled .sp-palette-container {
display: none;
}
.sp-initial-disabled .sp-initial {
display: none;
}
/* Gradients for hue, saturation and value instead of images. Not pretty... but it works */
.sp-sat {
background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#FFF), to(rgba(204, 154, 129, 0)));
background-image: -webkit-linear-gradient(left, #FFF, rgba(204, 154, 129, 0));
background-image: -moz-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
background-image: -o-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
background-image: -ms-linear-gradient(left, #fff, rgba(204, 154, 129, 0));
background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0));
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)";
filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81');
}
.sp-val {
background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#000000), to(rgba(204, 154, 129, 0)));
background-image: -webkit-linear-gradient(bottom, #000000, rgba(204, 154, 129, 0));
background-image: -moz-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
background-image: -o-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
background-image: -ms-linear-gradient(bottom, #000, rgba(204, 154, 129, 0));
background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0));
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)";
filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000');
}
.sp-hue {
background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000));
background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
}
/* IE filters do not support multiple color stops.
Generate 6 divs, line them up, and do two color gradients for each.
Yes, really.
*/
.sp-1 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00');
}
.sp-2 {
height:16%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00');
}
.sp-3 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff');
}
.sp-4 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff');
}
.sp-5 {
height:16%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff');
}
.sp-6 {
height:17%;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000');
}
.sp-hidden {
display: none !important;
}
/* Clearfix hack */
.sp-cf:before, .sp-cf:after { content: ""; display: table; }
.sp-cf:after { clear: both; }
.sp-cf { *zoom: 1; }
/* Mobile devices, make hue slider bigger so it is easier to slide */
@media (max-device-width: 480px) {
.sp-color { right: 40%; }
.sp-hue { left: 63%; }
.sp-fill { padding-top: 60%; }
}
.sp-dragger {
border-radius: 5px;
height: 5px;
width: 5px;
border: 1px solid #fff;
background: #000;
cursor: pointer;
position:absolute;
top:0;
left: 0;
}
.sp-slider {
position: absolute;
top:0;
cursor:pointer;
height: 3px;
left: -1px;
right: -1px;
border: 1px solid #000;
background: white;
opacity: .8;
}
/*
Theme authors:
Here are the basic themeable display options (colors, fonts, global widths).
See http://bgrins.github.io/spectrum/themes/ for instructions.
*/
.sp-container {
border-radius: 0;
background-color: #ECECEC;
border: solid 1px #f0c49B;
padding: 0;
}
.sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue, .sp-clear
{
font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
.sp-top
{
margin-bottom: 3px;
}
.sp-color, .sp-hue, .sp-clear
{
border: solid 1px #666;
}
/* Input */
.sp-input-container {
float:right;
width: 100px;
margin-bottom: 4px;
}
.sp-initial-disabled .sp-input-container {
width: 100%;
}
.sp-input {
font-size: 12px !important;
border: 1px inset;
padding: 4px 5px;
margin: 0;
width: 100%;
background:transparent;
border-radius: 3px;
color: #222;
}
.sp-input:focus {
border: 1px solid orange;
}
.sp-input.sp-validation-error
{
border: 1px solid red;
background: #fdd;
}
.sp-picker-container , .sp-palette-container
{
float:left;
position: relative;
padding: 10px;
padding-bottom: 300px;
margin-bottom: -290px;
}
.sp-picker-container
{
width: 172px;
border-left: solid 1px #fff;
}
/* Palettes */
.sp-palette-container
{
border-right: solid 1px #ccc;
}
.sp-palette .sp-thumb-el {
display: block;
position:relative;
float:left;
width: 24px;
height: 15px;
margin: 3px;
cursor: pointer;
border:solid 2px transparent;
}
.sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active {
border-color: orange;
}
.sp-thumb-el
{
position:relative;
}
/* Initial */
.sp-initial
{
float: left;
border: solid 1px #333;
}
.sp-initial span {
width: 30px;
height: 25px;
border:none;
display:block;
float:left;
margin:0;
}
.sp-initial .sp-clear-display {
background-position: center;
}
/* Buttons */
.sp-button-container {
float: right;
}
/* Replacer (the little preview div that shows up instead of the <input>) */
.sp-replacer {
margin:0;
overflow:hidden;
cursor:pointer;
padding: 4px;
display:inline-block;
*zoom: 1;
*display: inline;
border: solid 1px #91765d;
background: #eee;
color: #333;
vertical-align: middle;
}
.sp-replacer:hover, .sp-replacer.sp-active {
border-color: #F0C49B;
color: #111;
}
.sp-replacer.sp-disabled {
cursor:default;
border-color: silver;
color: silver;
}
.sp-dd {
padding: 2px 0;
height: 16px;
line-height: 16px;
float:left;
font-size:10px;
}
.sp-preview
{
position:relative;
width:25px;
height: 20px;
border: solid 1px #222;
margin-right: 5px;
float:left;
z-index: 0;
}
.sp-palette
{
*width: 220px;
max-width: 220px;
}
.sp-palette .sp-thumb-el
{
width:16px;
height: 16px;
margin:2px 1px;
border: solid 1px #d0d0d0;
}
.sp-container
{
padding-bottom:0;
}
/* Buttons: http://hellohappy.org/css3-buttons/ */
.sp-container button {
background-color: #eeeeee;
background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc);
background-image: -moz-linear-gradient(top, #eeeeee, #cccccc);
background-image: -ms-linear-gradient(top, #eeeeee, #cccccc);
background-image: -o-linear-gradient(top, #eeeeee, #cccccc);
background-image: linear-gradient(to bottom, #eeeeee, #cccccc);
border: 1px solid #ccc;
border-bottom: 1px solid #bbb;
border-radius: 3px;
color: #333;
font-size: 14px;
line-height: 1;
padding: 5px 4px;
text-align: center;
text-shadow: 0 1px 0 #eee;
vertical-align: middle;
}
.sp-container button:hover {
background-color: #dddddd;
background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb);
background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb);
background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb);
background-image: -o-linear-gradient(top, #dddddd, #bbbbbb);
background-image: linear-gradient(to bottom, #dddddd, #bbbbbb);
border: 1px solid #bbb;
border-bottom: 1px solid #999;
cursor: pointer;
text-shadow: 0 1px 0 #ddd;
}
.sp-container button:active {
border: 1px solid #aaa;
border-bottom: 1px solid #888;
-webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
-moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
-ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
-o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee;
}
.sp-cancel
{
font-size: 11px;
color: #d93f3f !important;
margin:0;
padding:2px;
margin-right: 5px;
vertical-align: middle;
text-decoration:none;
}
.sp-cancel:hover
{
color: #d93f3f !important;
text-decoration: underline;
}
.sp-palette span:hover, .sp-palette span.sp-thumb-active
{
border-color: #000;
}
.sp-preview, .sp-alpha, .sp-thumb-el
{
position:relative;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==);
}
.sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner
{
display:block;
position:absolute;
top:0;left:0;bottom:0;right:0;
}
.sp-palette .sp-thumb-inner
{
background-position: 50% 50%;
background-repeat: no-repeat;
}
.sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner
{
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=);
}
.sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner
{
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=);
}
.sp-clear-display {
background-repeat:no-repeat;
background-position: center;
background-image: url(data:image/gif;base64,R0lGODlhFAAUAPcAAAAAAJmZmZ2dnZ6enqKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq/Hx8fLy8vT09PX19ff39/j4+Pn5+fr6+vv7+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAUABQAAAihAP9FoPCvoMGDBy08+EdhQAIJCCMybCDAAYUEARBAlFiQQoMABQhKUJBxY0SPICEYHBnggEmDKAuoPMjS5cGYMxHW3IiT478JJA8M/CjTZ0GgLRekNGpwAsYABHIypcAgQMsITDtWJYBR6NSqMico9cqR6tKfY7GeBCuVwlipDNmefAtTrkSzB1RaIAoXodsABiZAEFB06gIBWC1mLVgBa0AAOw==);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet.draw popup event example</title>
<script src="libs/leaflet-src.js"></script>
<link rel="stylesheet" href="libs/leaflet.css" />
<script src="../../src/Leaflet.draw.js"></script>
<script src="../../src/Leaflet.Draw.Event.js"></script>
<link rel="stylesheet" href="../../src/leaflet.draw.css" />
<script src="../../src/Toolbar.js"></script>
<script src="../../src/Tooltip.js"></script>
<script src="../../src/ext/GeometryUtil.js"></script>
<script src="../../src/ext/LatLngUtil.js"></script>
<script src="../../src/ext/LineUtil.Intersect.js"></script>
<script src="../../src/ext/Polygon.Intersect.js"></script>
<script src="../../src/ext/Polyline.Intersect.js"></script>
<script src="../../src/ext/TouchEvents.js"></script>
<script src="../../src/draw/DrawToolbar.js"></script>
<script src="../../src/draw/handler/Draw.Feature.js"></script>
<script src="../../src/draw/handler/Draw.SimpleShape.js"></script>
<script src="../../src/draw/handler/Draw.Polyline.js"></script>
<script src="../../src/draw/handler/Draw.Marker.js"></script>
<script src="../../src/draw/handler/Draw.CircleMarker.js"></script>
<script src="../../src/draw/handler/Draw.Circle.js"></script>
<script src="../../src/draw/handler/Draw.Polygon.js"></script>
<script src="../../src/draw/handler/Draw.Rectangle.js"></script>
<script src="../../src/edit/EditToolbar.js"></script>
<script src="../../src/edit/handler/EditToolbar.Edit.js"></script>
<script src="../../src/edit/handler/EditToolbar.Delete.js"></script>
<script src="../../src/Control.Draw.js"></script>
<script src="../../src/edit/handler/Edit.Poly.js"></script>
<script src="../../src/edit/handler/Edit.SimpleShape.js"></script>
<script src="../../src/edit/handler/Edit.Marker.js"></script>
<script src="../../src/edit/handler/Edit.CircleMarker.js"></script>
<script src="../../src/edit/handler/Edit.Circle.js"></script>
<script src="../../src/edit/handler/Edit.Rectangle.js"></script>
</head>
<body>
<div id="map" style="width: 800px; height: 600px; border: 1px solid #ccc"></div>
<script>
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
osm = L.tileLayer(osmUrl, {maxZoom: 18, attribution: osmAttrib}),
map = new L.Map('map', {center: new L.LatLng(29.9792, 31.1344), zoom: 15}),
drawnItems = L.featureGroup().addTo(map);
L.control.layers({
"osm": osm.addTo(map),
"google": L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', {
attribution: 'google'
})
}, {'drawlayer':drawnItems}, { position: 'topright', collapsed: false }).addTo(map);
map.addControl(new L.Control.Draw({
edit: {
featureGroup: drawnItems,
poly : {
allowIntersection : false
}
},
draw: {
polygon : {
allowIntersection: false,
showArea:true
}
}
}));
// Truncate value based on number of decimals
var _round = function(num, len) {
return Math.round(num*(Math.pow(10, len)))/(Math.pow(10, len));
};
// Helper method to format LatLng object (x.xxxxxx, y.yyyyyy)
var strLatLng = function(latlng) {
return "("+_round(latlng.lat, 6)+", "+_round(latlng.lng, 6)+")";
};
// Generate popup content based on layer type
// - Returns HTML string, or null if unknown object
var getPopupContent = function(layer) {
// Marker - add lat/long
if (layer instanceof L.Marker || layer instanceof L.CircleMarker) {
return strLatLng(layer.getLatLng());
// Circle - lat/long, radius
} else if (layer instanceof L.Circle) {
var center = layer.getLatLng(),
radius = layer.getRadius();
return "Center: "+strLatLng(center)+"<br />"
+"Radius: "+_round(radius, 2)+" m";
// Rectangle/Polygon - area
} else if (layer instanceof L.Polygon) {
var latlngs = layer._defaultShape ? layer._defaultShape() : layer.getLatLngs(),
area = L.GeometryUtil.geodesicArea(latlngs);
return "Area: "+L.GeometryUtil.readableArea(area, true);
// Polyline - distance
} else if (layer instanceof L.Polyline) {
var latlngs = layer._defaultShape ? layer._defaultShape() : layer.getLatLngs(),
distance = 0;
if (latlngs.length < 2) {
return "Distance: N/A";
} else {
for (var i = 0; i < latlngs.length-1; i++) {
distance += latlngs[i].distanceTo(latlngs[i+1]);
}
return "Distance: "+_round(distance, 2)+" m";
}
}
return null;
};
// Object created - bind popup to layer, add to feature group
map.on(L.Draw.Event.CREATED, function(event) {
var layer = event.layer;
var content = getPopupContent(layer);
if (content !== null) {
layer.bindPopup(content);
}
drawnItems.addLayer(layer);
});
// Object(s) edited - update popups
map.on(L.Draw.Event.EDITED, function(event) {
var layers = event.layers,
content = null;
layers.eachLayer(function(layer) {
content = getPopupContent(layer);
if (content !== null) {
layer.setPopupContent(content);
}
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,171 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet.draw drawing with snapping</title>
<link rel="stylesheet" href="libs/leaflet.css" />
<link rel="stylesheet" href="../../src/leaflet.draw.css" />
<script src="libs/leaflet-src.js"></script>
<script src="../../src/Leaflet.draw.js"></script>
<script src="../../src/Leaflet.Draw.Event.js"></script>
<script src="../../src/edit/handler/Edit.Poly.js"></script>
<script src="../../src/edit/handler/Edit.SimpleShape.js"></script>
<script src="../../src/edit/handler/Edit.Rectangle.js"></script>
<script src="../../src/edit/handler/Edit.Marker.js"></script>
<script src="../../src/edit/handler/Edit.CircleMarker.js"></script>
<script src="../../src/edit/handler/Edit.Circle.js"></script>
<script src="../../src/draw/handler/Draw.Feature.js"></script>
<script src="../../src/draw/handler/Draw.Polyline.js"></script>
<script src="../../src/draw/handler/Draw.Polygon.js"></script>
<script src="../../src/draw/handler/Draw.SimpleShape.js"></script>
<script src="../../src/draw/handler/Draw.Rectangle.js"></script>
<script src="../../src/draw/handler/Draw.Marker.js"></script>
<script src="../../src/draw/handler/Draw.CircleMarker.js"></script>
<script src="../../src/draw/handler/Draw.Circle.js"></script>
<script src="../../src/ext/TouchEvents.js"></script>
<script src="../../src/ext/LatLngUtil.js"></script>
<script src="../../src/ext/GeometryUtil.js"></script>
<script src="../../src/ext/LineUtil.Intersect.js"></script>
<script src="../../src/ext/Polyline.Intersect.js"></script>
<script src="../../src/ext/Polygon.Intersect.js"></script>
<script src="../../src/Control.Draw.js"></script>
<script src="../../src/Tooltip.js"></script>
<script src="../../src/Toolbar.js"></script>
<script src="../../src/draw/DrawToolbar.js"></script>
<script src="../../src/edit/EditToolbar.js"></script>
<script src="../../src/edit/handler/EditToolbar.Edit.js"></script>
<script src="../../src/edit/handler/EditToolbar.Delete.js"></script>
<script src="libs/leaflet.snap.js"></script>
<script src="libs/leaflet.geometryutil.js"></script>
</head>
<body>
<div id="map" style="width: 800px; height: 600px; border: 1px solid #ccc"></div>
<button id="changeColor">Rectangle -> Blue</button>
<script>
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> contributors',
osm = L.tileLayer(osmUrl, {maxZoom: 18, attribution: osmAttrib}),
map = new L.Map('map', {layers: [osm], center: new L.LatLng(48.48988, 1.39638), zoom: 14 });
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
// Set the title to show on the polygon button
L.drawLocal.draw.toolbar.buttons.polygon = 'Draw a sexy polygon!';
var drawControl = new L.Control.Draw({
position: 'topright',
draw: {
polyline: true,
polygon: true,
circle: false,
marker: true
},
edit: {
featureGroup: drawnItems,
remove: true
}
});
map.addControl(drawControl);
var guides =
L.polyline([
[48.505431207150885, 1.3999843597412107],
[48.50335551764662, 1.398911476135254],
[48.50173471468476, 1.3994693756103516],
[48.49974418399956, 1.3991689682006836],
[48.49684355649577, 1.3993835449218748],
[48.4956206932084, 1.398611068725586],
[48.49465375716902, 1.3980531692504883],
[48.49419872206354, 1.3975811004638672],
[48.492406981637345, 1.3971948623657227],
[48.49156797030711, 1.396486759185791],
[48.49067206152607, 1.3961219787597656],
[48.48988, 1.39638],
[48.489342389949364, 1.394963264465332],
[48.48864554279267, 1.3944590091705322],
[48.487628697617744, 1.3940191268920896],
[48.485666057669334, 1.3944482803344727],
[48.48541005555473, 1.3942551612854002],
[48.48461359626773, 1.3942766189575195],
[48.483489998505746, 1.3933539390563965],
[48.48164098598135, 1.3928818702697754],
[48.480232846617845, 1.3912296295166016],
[48.479450530080534, 1.3906073570251463],
[48.478511734309954, 1.3902640342712402],
[48.47714618217502, 1.389319896697998],
[48.47600819398379, 1.388998031616211]
], {
weight: 5,
color: 'red',
opacity: 1.0
}).addTo(map);
var marker = L.marker([48.488, 1.395]).addTo(map);
marker.snapediting = new L.Handler.MarkerSnap(map, marker);
marker.snapediting.addGuideLayer(guides);
marker.snapediting.enable();
var road = L.polyline([
[48.48922, 1.40033],
[48.48935, 1.39981],
[48.48948, 1.3976],
[48.48986, 1.39634]
], {
color: 'green',
opacity: 1.0
}).addTo(map);
road.snapediting = new L.Handler.PolylineSnap(map, road);
road.snapediting.addGuideLayer(guides);
road.snapediting.enable();
marker.snapediting.addGuideLayer(road);
var guideLayers = [guides, road];
drawControl.setDrawingOptions({
polyline: { guideLayers: guideLayers },
polygon: { guideLayers: guideLayers, snapDistance: 5 },
marker: { guideLayers: guideLayers, snapVertices: false },
circlemarker: { guideLayers: guideLayers },
rectangle: false,
circle: false
});
map.on(L.Draw.Event.CREATED, function (e) {
var type = e.layerType,
layer = e.layer;
if (type === 'marker') {
layer.bindPopup('A popup!');
}
drawnItems.addLayer(layer);
});
map.on(L.Draw.Event.EDITED, function (e) {
var layers = e.layers;
var countOfEditedLayers = 0;
layers.eachLayer(function(layer) {
countOfEditedLayers++;
});
console.log("Edited " + countOfEditedLayers + " layers");
});
L.DomUtil.get('changeColor').onclick = function () {
drawControl.setDrawingOptions({ rectangle: { shapeOptions: { color: '#004a80' } } });
};
</script>
</body>
</html>