Skip to content

Commit

Permalink
Merge pull request #2290 from Leaflet/vectors2
Browse files Browse the repository at this point in the history
Vectors 2.0
  • Loading branch information
mourner committed Dec 18, 2013
2 parents 98c6178 + 9de557d commit c99d4b1
Show file tree
Hide file tree
Showing 33 changed files with 1,494 additions and 1,461 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ An in-progress version being developed on the `master` branch. Includes `stable`

This version contains a lot of beneficial but potentially breaking changes (especially if you're a plugin author), so please read through the changes carefully before upgrading.

### Vectors refactoring

Leaflet vector layers rendering got a major rewrite, making it possible to switch between rendering backends (Canvas, SVG) dynamically (and even use both for different layers at the same time), have more than one physical pane for vector layers, significantly improving Canvas performance and adding retina support, getting rid of ugly hacks and generally making the code much cleaner.

* Added `Renderer` layer (inherited by SVG/Canvas implementations) that handles renderer bounds and zoom animation.
* Added `SVG` & `Canvas` classes that now contain all renderer-specific code for vector layers and can be added as layers to the map.
* All vector layer classes (`Path`, `Polyline`, etc.) now don't contain any renderer-specific code and instead can be passed a renderer layer to use as `renderer` in options;
* Removed `MultiPolyline` and `MultiPolygon` classes since multiple rings are now handled by `Polyline` and `Polygon` classes respectively. Layers with multiple rings now perform much better (since each is now physically a single path object instead of being a `FeatureGroup` of layers).
* Dramatically improved performance of interactive Canvas layers. Mouse events work much faster (due to improved hit detection algorithms), and layers get updated many times faster (with partial redraws instead of redrawing the whole Canvas).
* Added retina support for Canvas layers.
* Improved default vector layer styles.
* Added `Polyline` and `Polygon` `getCenter` for getting centroids.
* Respectively, improved `Polyline` and `Polygon` popup positioning when calling `openPopup`.
* Improved `Polyline`/`Polygon` `getBounds` to be cached (so it works much faster).
* Changed `Circle` to inherit from `CircleMarker` and not the other way.
* Fixed `GeoJSON` `resetStyle` to work properly in case `style` option wasn't set.

### Layers refactoring

All Leaflet layers (including markers, popups, tile and vector layers) have been refactored to have a common parent, `Layer` class, that shares the basic logic of adding and removing. The leads to the following changes (documented in PR [#2266](https://github.com/Leaflet/Leaflet/pull/2266)):
Expand Down Expand Up @@ -58,6 +75,8 @@ These changes were targeted at removing any hardcoded projection-specific logic
* Significantly [improved](http://jsperf.com/leaflet-parsefloat-in-latlng/2) `LatLng` creation performance (8x).
* Added `Evented` class that now many Leaflet classes inherit from (instead of mixing in events methods in each class), while keeping the old `includes: L.Mixin.Events` syntax available. [#2310](https://github.com/Leaflet/Leaflet/pull/2310)
* Improved `Class` `options` to be prototypically inherited instead of copied over in children classes, which leads to more efficient memory use, faster object construction times and ability to change parent options that get propagated to all children automatically (by [@jfirebaugh](https://github.com/jfirebaugh)). [#2300](https://github.com/Leaflet/Leaflet/pull/2300) [#2294](https://github.com/Leaflet/Leaflet/issues/2294)
* Fixed `DomUtil` `on` to make it possible to add the same listener to the same element for multiple contexts.
* Changed `DomUtil` `off` to require `context` attribute if you remove a listener that was previously added with a particular context.
* Added `{r}` variable to `TileLayer` `url` for adding `"@2x"` prefix on Retina-enabled devices (by [@urbaniak](https://github.com/urbaniak)). [#2296](https://github.com/Leaflet/Leaflet/pull/2296)
* Removed `DomUtil.getViewportOffset` method as it is no longer necessary.
* Added `Point` `ceil` method (by [@perliedman](https://github.com/perliedman)).
Expand Down
63 changes: 29 additions & 34 deletions build/deps.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,25 +84,17 @@ 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.',
src: [
'layer/vector/Renderer.js',
'layer/vector/Path.js',
'layer/vector/Path.Popup.js'
],
desc: 'Vector rendering core.',
heading: 'Vector layers'
},

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+).'
},

Polyline: {
src: ['geometry/LineUtil.js',
'layer/vector/Polyline.js'],
Expand All @@ -117,42 +109,45 @@ var deps = {
desc: 'Polygon overlays.'
},

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.']
},

CircleMarker: {
src: ['layer/vector/CircleMarker.js'],
deps: ['Path'],
desc: 'Circle overlays with a constant pixel radius.'
},

Circle: {
src: ['layer/vector/Circle.js'],
deps: ['Path'],
deps: ['CircleMarker'],
desc: 'Circle overlays (with radius in meters).'
},

CircleMarker: {
src: ['layer/vector/CircleMarker.js'],
deps: ['Circle'],
desc: 'Circle overlays with a constant pixel radius.'
SVG: {
src: ['layer/vector/SVG.js'],
deps: ['Path'],
desc: 'SVG backend for vector layers.'
},

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)'
VML: {
src: ['layer/vector/SVG.VML.js'],
deps: ['SVG'],
desc: 'VML fallback for vector layers in IE7-8.'
},

Canvas: {
src: ['layer/vector/Canvas.js'],
deps: ['Path'],
desc: 'Canvas backend for vector layers.'
},

GeoJSON: {
src: ['layer/GeoJSON.js'],
deps: ['CircleMarker', 'Marker', 'MultiPoly', 'FeatureGroup'],
deps: ['Polygon', 'Circle', 'CircleMarker', 'Marker', 'FeatureGroup'],
desc: 'GeoJSON layer, parses the data and adds corresponding layers above.'
},

Expand Down
55 changes: 55 additions & 0 deletions debug/vector/vector2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!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});

var map = new L.Map('map', {layers: [cloudmade]});

map.addLayer(L.marker(route[0]));
map.addLayer(L.marker(route[route.length - 1]));

var canvas = L.canvas();

var poly = L.polyline([
[[60, 30], [60, 50], [40, 50]],
[[20, 50], [20, 70], [0, 70]]
], {color: 'red'}).addTo(map).bindPopup('Hello SVG');

var path = L.polygon([
[route, [[50.5, 30.5], [50.5, 40], [40, 40]]],
[[[20, 0], [20, 40], [0, 40]]]
], {renderer: canvas}).addTo(map).bindPopup('Hello Canvas');

var circle = L.circle([35, 0], 700000, {color: 'green', renderer: canvas}).addTo(map).bindPopup('Hello Circle');
var circleMarker = L.circleMarker([35, 30], {color: 'magenta', radius: 30}).addTo(map);

map.setView([36, 52], 3);

var layersControl = new L.Control.Layers({
}, {
'poly': poly,
'path': path,
// 'rect': rect,
'circle': circle,
'circleMarker': circleMarker,
'canvas': canvas,
'svg': L.SVG.instance,
}, {collapsed: false});
map.addControl(layersControl);
</script>
</body>
</html>
6 changes: 5 additions & 1 deletion dist/leaflet.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
.leaflet-shadow-pane,
.leaflet-marker-pane,
.leaflet-popup-pane,
.leaflet-overlay-pane svg,
.leaflet-map-pane svg,
.leaflet-map-pane canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
Expand Down Expand Up @@ -65,6 +66,9 @@
.leaflet-marker-pane { z-index: 6; }
.leaflet-popup-pane { z-index: 7; }

.leaflet-map-pane canvas { z-index: 1; }
.leaflet-map-pane svg { z-index: 2; }

.leaflet-vml-shape {
width: 1px;
height: 1px;
Expand Down
4 changes: 2 additions & 2 deletions spec/suites/geometry/PolyUtilSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ describe('PolyUtil', function () {
}

expect(clipped).to.eql([
new L.Point(7.5, 10),
new L.Point(8, 10),
new L.Point(5, 5),
new L.Point(10, 7.5),
new L.Point(10, 8),
new L.Point(10, 10)
]);
});
Expand Down
12 changes: 6 additions & 6 deletions spec/suites/layer/GeoJSONSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ describe("L.Polyline#toGeoJSON", function () {
});
});

describe("L.MultiPolyline#toGeoJSON", function () {
describe("L.Polyline (multi) #toGeoJSON", function () {
it("returns a 2D MultiLineString object", function () {
var multiPolyline = new L.MultiPolyline([[[10, 20], [2, 5]], [[1, 2], [3, 4]]]);
var multiPolyline = new L.Polyline([[[10, 20], [2, 5]], [[1, 2], [3, 4]]]);
expect(multiPolyline.toGeoJSON().geometry).to.eql({
type: 'MultiLineString',
coordinates: [
Expand All @@ -108,7 +108,7 @@ describe("L.MultiPolyline#toGeoJSON", function () {
});

it("returns a 3D MultiLineString object", function () {
var multiPolyline = new L.MultiPolyline([[[10, 20, 30], [2, 5, 10]], [[1, 2, 3], [4, 5, 6]]]);
var multiPolyline = new L.Polyline([[[10, 20, 30], [2, 5, 10]], [[1, 2, 3], [4, 5, 6]]]);
expect(multiPolyline.toGeoJSON().geometry).to.eql({
type: 'MultiLineString',
coordinates: [
Expand Down Expand Up @@ -159,9 +159,9 @@ describe("L.Polygon#toGeoJSON", function () {
});
});

describe("L.MultiPolygon#toGeoJSON", function () {
describe("L.Polygon (multi) #toGeoJSON", function () {
it("returns a 2D MultiPolygon object", function () {
var multiPolygon = new L.MultiPolygon([[[1, 2], [3, 4], [5, 6]]]);
var multiPolygon = new L.Polygon([[[1, 2], [3, 4], [5, 6]]]);
expect(multiPolygon.toGeoJSON().geometry).to.eql({
type: 'MultiPolygon',
coordinates: [
Expand All @@ -171,7 +171,7 @@ describe("L.MultiPolygon#toGeoJSON", function () {
});

it("returns a 3D MultiPolygon object", function () {
var multiPolygon = new L.MultiPolygon([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]);
var multiPolygon = new L.Polygon([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]);
expect(multiPolygon.toGeoJSON().geometry).to.eql({
type: 'MultiPolygon',
coordinates: [
Expand Down
13 changes: 9 additions & 4 deletions spec/suites/layer/vector/PolygonSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ describe('Polygon', function () {

var polygon = new L.Polygon(originalLatLngs);

//getLatLngs() returns only external ring
expect(polygon.getLatLngs()).to.eql([L.latLng([0, 10]), L.latLng([10, 10]), L.latLng([10, 0])]);
// getLatLngs() returns both rings
expect(polygon.getLatLngs()).to.eql([
[L.latLng([0, 10]), L.latLng([10, 10]), L.latLng([10, 0])],
[L.latLng([2, 3]), L.latLng([2, 4]), L.latLng([3, 4])]
]);
});
});

Expand Down Expand Up @@ -68,8 +71,10 @@ describe('Polygon', function () {
var polygon = new L.Polygon([]);
polygon.setLatLngs(latLngs);

//getLatLngs() returns only external ring
expect(polygon.getLatLngs()).to.eql([L.latLng([0, 10]), L.latLng([10, 10]), L.latLng([10, 0])]);
expect(polygon.getLatLngs()).to.eql([
[L.latLng([0, 10]), L.latLng([10, 10]), L.latLng([10, 0])],
[L.latLng([2, 3]), L.latLng([2, 4]), L.latLng([3, 4])]
]);
});
});

Expand Down
8 changes: 4 additions & 4 deletions src/dom/DomEvent.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

L.DomEvent = {
/* inspired by John Resig, Dean Edwards and YUI addEvent implementations */
addListener: function (obj, type, fn, context) { // (HTMLElement, String, Function[, Object])
addListener: function (obj, type, fn, context) {

var id = L.stamp(fn),
var id = L.stamp(fn) + (context ? '_' + L.stamp(context) : ''),
key = '_leaflet_' + type + id;

if (obj[key]) { return this; }
Expand Down Expand Up @@ -56,9 +56,9 @@ L.DomEvent = {
return this;
},

removeListener: function (obj, type, fn) { // (HTMLElement, String, Function)
removeListener: function (obj, type, fn, context) {

var id = L.stamp(fn),
var id = L.stamp(fn) + (context ? '_' + L.stamp(context) : ''),
key = '_leaflet_' + type + id,
handler = obj[key];

Expand Down
4 changes: 2 additions & 2 deletions src/dom/Draggable.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ L.Draggable = L.Evented.extend({

for (var i in L.Draggable.MOVE) {
L.DomEvent
.off(document, L.Draggable.MOVE[i], this._onMove)
.off(document, L.Draggable.END[i], this._onUp);
.off(document, L.Draggable.MOVE[i], this._onMove, this)
.off(document, L.Draggable.END[i], this._onUp, this);
}

L.DomUtil.enableImageDrag();
Expand Down
21 changes: 16 additions & 5 deletions src/geometry/LineUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,17 +136,27 @@ L.LineUtil = {
var dx = b.x - a.x,
dy = b.y - a.y,
min = bounds.min,
max = bounds.max;
max = bounds.max,
x, y;

if (code & 8) { // top
return new L.Point(a.x + dx * (max.y - a.y) / dy, max.y);
x = a.x + dx * (max.y - a.y) / dy;
y = max.y;

} else if (code & 4) { // bottom
return new L.Point(a.x + dx * (min.y - a.y) / dy, min.y);
x = a.x + dx * (min.y - a.y) / dy;
y = min.y;

} else if (code & 2) { // right
return new L.Point(max.x, a.y + dy * (max.x - a.x) / dx);
x = max.x;
y = a.y + dy * (max.x - a.x) / dx;

} else if (code & 1) { // left
return new L.Point(min.x, a.y + dy * (min.x - a.x) / dx);
x = min.x;
y = a.y + dy * (min.x - a.x) / dx;
}

return new L.Point(x, y, true);
},

_getBitCode: function (/*Point*/ p, bounds) {
Expand All @@ -157,6 +167,7 @@ L.LineUtil = {
} else if (p.x > bounds.max.x) { // right
code |= 2;
}

if (p.y < bounds.min.y) { // bottom
code |= 4;
} else if (p.y > bounds.max.y) { // top
Expand Down
Loading

0 comments on commit c99d4b1

Please sign in to comment.