Skip to content

Commit 2545ae5

Browse files
committed
Classify polygon rings to distinguish inner polygon rings and multipolygons
1 parent df74de2 commit 2545ae5

14 files changed

Lines changed: 168 additions & 24 deletions

fixtures.js

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ var mapnik = require('mapnik');
22
var path = require('path');
33
var fs = require('fs');
44

5-
mapnik.register_datasource(path.join(mapnik.settings.paths.input_plugins,'geojson.input'));
5+
mapnik.register_datasource(path.join(mapnik.settings.paths.input_plugins, 'geojson.input'));
66

77
var fixtures = {
88
"zero-point": {
@@ -31,6 +31,19 @@ var fixtures = {
3131
}
3232
]
3333
},
34+
"zero-polygon": {
35+
"type": "FeatureCollection",
36+
"features": [
37+
{
38+
"type": "Feature",
39+
"geometry": {
40+
"type": "MultiPolygon",
41+
"coordinates": []
42+
},
43+
"properties": {}
44+
}
45+
]
46+
},
3447
"singleton-multi-point": {
3548
"type": "FeatureCollection",
3649
"features": [
@@ -57,6 +70,19 @@ var fixtures = {
5770
}
5871
]
5972
},
73+
"singleton-multi-polygon": {
74+
"type": "FeatureCollection",
75+
"features": [
76+
{
77+
"type": "Feature",
78+
"geometry": {
79+
"type": "MultiPolygon",
80+
"coordinates": [[[[0, 0], [1, 0], [1, 1], [0, 0]]]]
81+
},
82+
"properties": {}
83+
}
84+
]
85+
},
6086
"multi-point": {
6187
"type": "FeatureCollection",
6288
"features": [
@@ -82,11 +108,50 @@ var fixtures = {
82108
"properties": {}
83109
}
84110
]
111+
},
112+
"multi-polygon": {
113+
"type": "FeatureCollection",
114+
"features": [
115+
{
116+
"type": "Feature",
117+
"geometry": {
118+
"type": "MultiPolygon",
119+
"coordinates": [[[[0, 0], [1, 0], [1, 1], [0, 0]]], [[[0, 0], [-1, 0], [-1, -1], [0, 0]]]]
120+
},
121+
"properties": {}
122+
}
123+
]
124+
},
125+
"polygon-with-inner": {
126+
"type": "FeatureCollection",
127+
"features": [
128+
{
129+
"type": "Feature",
130+
"geometry": {
131+
"type": "Polygon",
132+
"coordinates": [[[-2, 2], [2, 2], [2, -2], [-2, -2], [-2, 2]], [[-1, 1], [1, 1], [1, -1], [-1, -1], [-1, 1]]]
133+
},
134+
"properties": {}
135+
}
136+
]
137+
},
138+
"stacked-multipolygon": {
139+
"type": "FeatureCollection",
140+
"features": [
141+
{
142+
"type": "Feature",
143+
"geometry": {
144+
"type": "MultiPolygon",
145+
"coordinates": [[[[-2, 2], [2, 2], [2, -2], [-2, -2], [-2, 2]]], [[[-1, 1], [1, 1], [1, -1], [-1, -1], [-1, 1]]]]
146+
},
147+
"properties": {}
148+
}
149+
]
85150
}
86151
}
87152

88153
for (var fixture in fixtures) {
89-
var vtile = new mapnik.VectorTile(0,0,0);
154+
var vtile = new mapnik.VectorTile(0, 0, 0);
90155
vtile.addGeoJSON(JSON.stringify(fixtures[fixture]), "geojson");
91156
fs.writeFileSync('./test/fixtures/' + fixture + '.pbf', vtile.getData());
92157
}

lib/vectortilefeature.js

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,10 @@ VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
131131
x0 = this.extent * x,
132132
y0 = this.extent * y,
133133
coords = this.loadGeometry(),
134-
type = VectorTileFeature.types[this.type];
134+
type = VectorTileFeature.types[this.type],
135+
i, j;
135136

136-
for (var i = 0; i < coords.length; i++) {
137-
var line = coords[i];
137+
function project(line) {
138138
for (var j = 0; j < line.length; j++) {
139139
var p = line[j], y2 = 180 - (p.y + y0) * 360 / size;
140140
line[j] = [
@@ -144,15 +144,36 @@ VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
144144
}
145145
}
146146

147-
if (type === 'Point' && coords.length === 1) {
148-
coords = coords[0][0];
149-
} else if (type === 'Point') {
150-
coords = coords.map(function (c) { return c[0]; });
151-
type = 'MultiPoint';
152-
} else if (type === 'LineString' && coords.length === 1) {
147+
switch (this.type) {
148+
case 1:
149+
var points = [];
150+
for (i = 0; i < coords.length; i++) {
151+
points[i] = coords[i][0];
152+
}
153+
coords = points;
154+
project(coords);
155+
break;
156+
157+
case 2:
158+
for (i = 0; i < coords.length; i++) {
159+
project(coords[i]);
160+
}
161+
break;
162+
163+
case 3:
164+
coords = classifyRings(coords);
165+
for (i = 0; i < coords.length; i++) {
166+
for (j = 0; j < coords[i].length; j++) {
167+
project(coords[i][j]);
168+
}
169+
}
170+
break;
171+
}
172+
173+
if (coords.length === 1) {
153174
coords = coords[0];
154-
} else if (type === 'LineString') {
155-
type = 'MultiLineString';
175+
} else {
176+
type = 'Multi' + type;
156177
}
157178

158179
var result = {
@@ -170,3 +191,43 @@ VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
170191

171192
return result;
172193
};
194+
195+
// classifies an array of rings into polygons with outer rings and holes
196+
197+
function classifyRings(rings) {
198+
var len = rings.length;
199+
200+
if (len <= 1) return [rings];
201+
202+
var polygons = [],
203+
polygon,
204+
ccw;
205+
206+
for (var i = 0; i < len; i++) {
207+
var area = signedArea(rings[i]);
208+
if (area === 0) continue;
209+
210+
if (ccw === undefined) ccw = area < 0;
211+
212+
if (ccw === area < 0) {
213+
if (polygon) polygons.push(polygon);
214+
polygon = [rings[i]];
215+
216+
} else {
217+
polygon.push(rings[i]);
218+
}
219+
}
220+
if (polygon) polygons.push(polygon);
221+
222+
return polygons;
223+
}
224+
225+
function signedArea(ring) {
226+
var sum = 0;
227+
for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
228+
p1 = ring[i];
229+
p2 = ring[j];
230+
sum += (p2.x - p1.x) * (p1.y + p2.y);
231+
}
232+
return sum;
233+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"benchmark": "^1.0.0",
1313
"coveralls": "~2.11.2",
1414
"istanbul": "~0.3.6",
15-
"mapnik": "^3.1.6",
15+
"mapnik": "^3.5.13",
1616
"jshint": "^2.6.3",
1717
"pbf": "^1.3.2",
1818
"tape": "~3.5.0",

test/fixtures/multi-line.pbf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
$
2-
geojson" � �
1+
$x
2+
geojson(� " � �
33
.- .+
4-
.-(� x
4+
.-

test/fixtures/multi-point.pbf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-

2-
geojson" � � .-(� x
1+
x
2+
geojson(� "� �.-

test/fixtures/multi-polygon.pbf

44 Bytes
Binary file not shown.

test/fixtures/multipolygon.pbf

56 Bytes
Binary file not shown.
48 Bytes
Binary file not shown.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-

2-
geojson" � �
3-
.-(� x
1+
x
2+
geojson(� " � �
3+
.-
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-

2-
geojson " � �(� x
1+
x
2+
geojson(�  " � �

0 commit comments

Comments
 (0)