initial working Path/Polyline implementation

This commit is contained in:
Vladimir Agafonkin 2013-12-12 11:36:22 -05:00
parent 568fd154e2
commit 076255f33f
5 changed files with 294 additions and 67 deletions

View File

@ -84,84 +84,87 @@ var deps = {
desc: 'Extends LayerGroup with mouse events and bindPopup method shared between layers.'
},
Path: {
src: ['layer/vector/Path.js',
'layer/vector/Path.SVG.js',
'layer/vector/Path.Popup.js'],
desc: 'Vector rendering core (SVG-powered), enables overlaying the map with SVG paths.',
heading: 'Vector layers'
},
// Path: {
// src: ['layer/vector/Path.js',
// 'layer/vector/Path.SVG.js',
// 'layer/vector/Path.Popup.js'],
// desc: 'Vector rendering core (SVG-powered), enables overlaying the map with SVG paths.',
// heading: 'Vector layers'
// },
PathVML: {
src: ['layer/vector/Path.VML.js'],
desc: 'VML fallback for vector rendering core (IE 6-8).'
},
// PathVML: {
// src: ['layer/vector/Path.VML.js'],
// desc: 'VML fallback for vector rendering core (IE 6-8).'
// },
PathCanvas: {
src: ['layer/vector/canvas/Path.Canvas.js'],
deps: ['Path', 'Polyline', 'Polygon', 'Circle'],
desc: 'Canvas fallback for vector rendering core (makes it work on Android 2+).'
},
// PathCanvas: {
// src: ['layer/vector/canvas/Path.Canvas.js'],
// deps: ['Path', 'Polyline', 'Polygon', 'Circle'],
// desc: 'Canvas fallback for vector rendering core (makes it work on Android 2+).'
// },
Polyline: {
src: ['geometry/LineUtil.js',
'layer/vector/Polyline.js'],
deps: ['Path'],
desc: 'Polyline overlays.'
},
// Polyline: {
// src: ['geometry/LineUtil.js',
// 'layer/vector/Polyline.js'],
// deps: ['Path'],
// desc: 'Polyline overlays.'
// },
Polygon: {
src: ['geometry/PolyUtil.js',
'layer/vector/Polygon.js'],
deps: ['Polyline'],
desc: 'Polygon overlays.'
},
// Polygon: {
// src: ['geometry/PolyUtil.js',
// 'layer/vector/Polygon.js'],
// deps: ['Polyline'],
// desc: 'Polygon overlays.'
// },
MultiPoly: {
src: ['layer/vector/MultiPoly.js'],
deps: ['FeatureGroup', 'Polyline', 'Polygon'],
desc: 'MultiPolygon and MultyPolyline layers.'
},
// MultiPoly: {
// src: ['layer/vector/MultiPoly.js'],
// deps: ['FeatureGroup', 'Polyline', 'Polygon'],
// desc: 'MultiPolygon and MultyPolyline layers.'
// },
Rectangle: {
src: ['layer/vector/Rectangle.js'],
deps: ['Polygon'],
desc: ['Rectangle overlays.']
},
// Rectangle: {
// src: ['layer/vector/Rectangle.js'],
// deps: ['Polygon'],
// desc: ['Rectangle overlays.']
// },
Circle: {
src: ['layer/vector/Circle.js'],
deps: ['Path'],
desc: 'Circle overlays (with radius in meters).'
},
// Circle: {
// src: ['layer/vector/Circle.js'],
// deps: ['Path'],
// desc: 'Circle overlays (with radius in meters).'
// },
CircleMarker: {
src: ['layer/vector/CircleMarker.js'],
deps: ['Circle'],
desc: 'Circle overlays with a constant pixel radius.'
},
// CircleMarker: {
// src: ['layer/vector/CircleMarker.js'],
// deps: ['Circle'],
// desc: 'Circle overlays with a constant pixel radius.'
// },
VectorsCanvas: {
src: ['layer/vector/canvas/Polyline.Canvas.js',
'layer/vector/canvas/Polygon.Canvas.js',
'layer/vector/canvas/Circle.Canvas.js',
'layer/vector/canvas/CircleMarker.Canvas.js'],
deps: ['PathCanvas', 'Polyline', 'Polygon', 'Circle', 'CircleMarker'],
desc: 'Canvas fallback for vector layers (polygons, polylines, circles, circlemarkers)'
},
// VectorsCanvas: {
// src: ['layer/vector/canvas/Polyline.Canvas.js',
// 'layer/vector/canvas/Polygon.Canvas.js',
// 'layer/vector/canvas/Circle.Canvas.js',
// 'layer/vector/canvas/CircleMarker.Canvas.js'],
// deps: ['PathCanvas', 'Polyline', 'Polygon', 'Circle', 'CircleMarker'],
// desc: 'Canvas fallback for vector layers (polygons, polylines, circles, circlemarkers)'
// },
Vector2: {
src: [
'layer/vector2/Renderer.js',
'layer/vector2/SVG.js'],
'layer/vector2/SVG.js',
'layer/vector2/Path.js',
'geometry/LineUtil.js',
'layer/vector2/Polyline.js'],
desc: 'New vector layers implementation.'
},
GeoJSON: {
src: ['layer/GeoJSON.js'],
deps: ['CircleMarker', 'Marker', 'MultiPoly', 'FeatureGroup'],
desc: 'GeoJSON layer, parses the data and adds corresponding layers above.'
},
// GeoJSON: {
// src: ['layer/GeoJSON.js'],
// deps: ['CircleMarker', 'Marker', 'MultiPoly', 'FeatureGroup'],
// desc: 'GeoJSON layer, parses the data and adds corresponding layers above.'
// },
MapDrag: {

40
debug/vector/vector2.html Normal file
View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet debug page</title>
<link rel="stylesheet" href="../../dist/leaflet.css" />
<link rel="stylesheet" href="../css/screen.css" />
<script type="text/javascript" src="../../build/deps.js"></script>
<script src="../leaflet-include.js"></script>
</head>
<body>
<div id="map" style="width: 800px; height: 600px; border: 1px solid #ccc"></div>
<script src="route.js"></script>
<script>
var cloudmadeUrl = 'http://{s}.tile.cloudmade.com/d4fc77ea4a63471cab2423e66626cbb6/997/256/{z}/{x}/{y}.png',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18});
for (var i = 0, latlngs = [], len = route.length; i < len; i++) {
latlngs.push(new L.LatLng(route[i][0], route[i][1]));
}
var svg = new L.SVG();
var path = new L.Polyline(latlngs, {renderer: svg});
var map = new L.Map('map', {layers: [cloudmade]});
map.fitBounds(new L.LatLngBounds(latlngs));
// map.addLayer(new L.Marker(latlngs[0]));
// map.addLayer(new L.Marker(latlngs[len - 1]));
map.addLayer(svg);
map.addLayer(path);
// path.bindPopup("Hello world");
</script>
</body>
</html>

61
src/layer/vector2/Path.js Normal file
View File

@ -0,0 +1,61 @@
L.Path = L.Layer.extend({
options: {
stroke: true,
color: '#0033ff',
// dashArray: null,
// lineCap: null,
// lineJoin: null,
weight: 5,
opacity: 0.5,
// fill: false,
// fillColor: null, same as color by default
fillOpacity: 0.2,
// className: ''
clickable: true
},
onAdd: function () {
this._renderer._initPath(this);
this._projectLatlngs();
this._updatePath();
this._renderer._addPath(this);
},
onRemove: function () {
this._renderer._removePath(this);
},
getEvents: function () {
return {
viewreset: this._projectLatlngs,
moveend: this._updatePath
};
},
redraw: function () {
if (this._map) {
this._projectLatlngs();
this._updatePath();
}
return this;
},
_updatePath: function () {
if (!this._map) { return; }
this._clipPoints();
this._simplifyPoints();
this._renderer._updatePoly(this);
}
});
L.polyline2 = function (latlngs, options) {
return new L.Polyline2(latlngs, options);
};

View File

@ -0,0 +1,107 @@
L.Polyline = L.Path.extend({
options: {
// how much to simplify the polyline on each zoom level
// more = better performance and smoother look, less = more accurate
smoothFactor: 1.0
// noClip: false
},
initialize: function (latlngs, options) {
L.setOptions(this, options);
this._latlngs = this._convertLatLngs(latlngs);
this._renderer = options.renderer;
},
getLatLngs: function () {
return this._latlngs;
},
setLatLngs: function (latlngs) {
this._latlngs = this._convertLatLngs(latlngs);
return this.redraw();
},
addLatLng: function (latlng) {
this._latlngs.push(L.latLng(latlng));
return this.redraw();
},
spliceLatLngs: function () {
var removed = [].splice.apply(this._latlngs, arguments);
this._convertLatLngs(this._latlngs, true);
this.redraw();
return removed;
},
// TODO closestLayerPoint?
getBounds: function () {
return new L.LatLngBounds(this.getLatLngs());
},
_convertLatLngs: function (latlngs, overwrite) {
var target = overwrite ? latlngs : [];
for (var i = 0, len = latlngs.length; i < len; i++) {
if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') { return; }
target[i] = L.latLng(latlngs[i]);
}
return target;
},
_projectLatlngs: function () {
this._originalPoints = [];
for (var i = 0, len = this._latlngs.length; i < len; i++) {
this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]);
}
},
_clipPoints: function () {
var points = this._originalPoints;
if (this.options.noClip) {
this._parts = [points];
return;
}
this._parts = [];
var parts = this._parts,
bounds = this._renderer._bounds,
len = points.length,
i, k, segment;
for (i = 0, k = 0; i < len - 1; i++) {
segment = L.LineUtil.clipSegment(points[i], points[i + 1], bounds, i);
if (!segment) { continue; }
parts[k] = parts[k] || [];
parts[k].push(segment[0]);
// if segment goes out of screen, or it's the last one, it's the end of the line part
if ((segment[1] !== points[i + 1]) || (i === len - 2)) {
parts[k].push(segment[1]);
k++;
}
}
},
// simplify each clipped part of the polyline
_simplifyPoints: function () {
var parts = this._parts,
tolerance = this.options.smoothFactor;
for (var i = 0, len = parts.length; i < len; i++) {
parts[i] = L.LineUtil.simplify(parts[i], tolerance);
}
}
});
L.polyline = function (latlngs, options) {
return new L.Polyline(latlngs, options);
};

View File

@ -1,7 +1,7 @@
L.SVG = L.Renderer.extend({
onAdd: function (map) {
onAdd: function () {
var container = this._container = L.SVG.create('svg');
if (this._zoomAnimated) {
@ -16,6 +16,9 @@ L.SVG = L.Renderer.extend({
L.DomUtil.remove(this._container);
},
// TODO bringToFront, bringToBack
// TODO events
_update: function () {
if (this._map._animatingZoom) { return; }
@ -42,13 +45,21 @@ L.SVG = L.Renderer.extend({
},
_initPath: function (layer) {
var path = layer._path = L.SVG.create('path');
layer._path = L.SVG.create('path');
if (layer.options.className) {
L.DomUtil.addClass(path, layer.options.className);
L.DomUtil.addClass(layer._path, layer.options.className);
}
this._container.appendChild(path);
this._updateStyle(layer);
},
_addPath: function (layer) {
this._container.appendChild(layer._path);
},
_removePath: function (layer) {
L.DomUtil.remove(layer._path);
},
_updateStyle: function (layer) {
@ -88,7 +99,7 @@ L.SVG = L.Renderer.extend({
},
_updatePoly: function (layer, closed) {
layer._path.setAttribute('d', L.SVG.pointsToPath(layer._points, closed) || 'M0 0');
layer._path.setAttribute('d', L.SVG.pointsToPath(layer._parts, closed) || 'M0 0');
}
});
@ -116,3 +127,8 @@ L.extend(L.SVG, {
});
L.Browser.svg = !!(document.createElementNS && L.SVG.create('svg').createSVGRect);
L.svg = function () {
return new L.SVG();
};