osm2geojson-lite 0.8.13 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,348 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const utils_1 = require("./utils");
4
+ const utils_2 = require("./utils");
5
+ const polytags_json_1 = require("./polytags.json");
6
+ class OsmObject {
7
+ constructor(type, id, refElems) {
8
+ this.type = type;
9
+ this.id = id;
10
+ this.refElems = refElems;
11
+ this.tags = {};
12
+ this.props = { id: this.getCompositeId() };
13
+ this.refCount = 0;
14
+ this.hasTag = false;
15
+ if (refElems) {
16
+ refElems.add(this.getCompositeId(), this);
17
+ }
18
+ }
19
+ addTags(tags) {
20
+ this.tags = Object.assign(this.tags, tags);
21
+ this.hasTag = tags ? true : false;
22
+ }
23
+ addTag(k, v) {
24
+ this.tags[k] = v;
25
+ this.hasTag = k ? true : false;
26
+ }
27
+ addProp(k, v) {
28
+ this.props[k] = v;
29
+ }
30
+ addProps(props) {
31
+ this.props = Object.assign(this.props, props);
32
+ }
33
+ getCompositeId() {
34
+ return `${this.type}/${this.id}`;
35
+ }
36
+ getProps() {
37
+ return Object.assign(this.props, this.tags);
38
+ }
39
+ toFeatureArray() {
40
+ return [];
41
+ }
42
+ }
43
+ class Node extends OsmObject {
44
+ constructor(id, refElems) {
45
+ super('node', id, refElems);
46
+ this.latLng = null;
47
+ }
48
+ setLatLng(latLng) {
49
+ this.latLng = latLng;
50
+ }
51
+ toFeatureArray() {
52
+ if (this.latLng) {
53
+ return [{
54
+ type: 'Feature',
55
+ id: this.getCompositeId(),
56
+ properties: this.getProps(),
57
+ geometry: {
58
+ type: 'Point',
59
+ coordinates: utils_1.strToFloat([this.latLng.lon, this.latLng.lat]),
60
+ },
61
+ }];
62
+ }
63
+ return [];
64
+ }
65
+ getLatLng() {
66
+ return this.latLng;
67
+ }
68
+ }
69
+ exports.Node = Node;
70
+ class Way extends OsmObject {
71
+ constructor(id, refElems) {
72
+ super('way', id, refElems);
73
+ this.latLngArray = [];
74
+ this.isPolygon = false;
75
+ }
76
+ addLatLng(latLng) {
77
+ this.latLngArray.push(latLng);
78
+ }
79
+ setLatLngArray(latLngArray) {
80
+ this.latLngArray = latLngArray;
81
+ }
82
+ addNodeRef(ref) {
83
+ const binder = new utils_2.LateBinder(this.latLngArray, function (id) {
84
+ const node = this.refElems.get(`node/${id}`);
85
+ if (node) {
86
+ node.refCount++;
87
+ return node.getLatLng();
88
+ }
89
+ }, this, [ref]);
90
+ this.latLngArray.push(binder);
91
+ this.refElems.addBinder(binder);
92
+ }
93
+ addTags(tags) {
94
+ super.addTags(tags);
95
+ for (const [k, v] of Object.entries(tags)) {
96
+ this.analyzeTag(k, v);
97
+ }
98
+ }
99
+ addTag(k, v) {
100
+ super.addTag(k, v);
101
+ this.analyzeTag(k, v);
102
+ }
103
+ toCoordsArray() {
104
+ return this.latLngArray.map((latLng) => [latLng.lon, latLng.lat]);
105
+ }
106
+ toFeatureArray() {
107
+ let coordsArray = this.toCoordsArray();
108
+ if (coordsArray.length > 1) {
109
+ coordsArray = utils_1.strToFloat(coordsArray);
110
+ const feature = {
111
+ type: 'Feature',
112
+ id: this.getCompositeId(),
113
+ properties: this.getProps(),
114
+ geometry: {
115
+ type: 'LineString',
116
+ coordinates: coordsArray,
117
+ },
118
+ };
119
+ if (this.isPolygon && utils_1.isRing(coordsArray)) {
120
+ if (utils_1.ringDirection(coordsArray) !== 'counterclockwise') {
121
+ coordsArray.reverse();
122
+ }
123
+ feature.geometry = {
124
+ type: 'Polygon',
125
+ coordinates: [coordsArray],
126
+ };
127
+ return [feature];
128
+ }
129
+ return [feature];
130
+ }
131
+ return [];
132
+ }
133
+ analyzeTag(k, v) {
134
+ const o = polytags_json_1.default[k];
135
+ if (o) {
136
+ this.isPolygon = true;
137
+ if (o.whitelist) {
138
+ this.isPolygon = o.whitelist.indexOf(v) >= 0 ? true : false;
139
+ }
140
+ else if (o.blacklist) {
141
+ this.isPolygon = o.blacklist.indexOf(v) >= 0 ? false : true;
142
+ }
143
+ }
144
+ }
145
+ }
146
+ exports.Way = Way;
147
+ class Relation extends OsmObject {
148
+ constructor(id, refElems) {
149
+ super('relation', id, refElems);
150
+ this.relations = [];
151
+ this.nodes = [];
152
+ this.bounds = undefined;
153
+ }
154
+ setBounds(bounds) {
155
+ this.bounds = bounds;
156
+ }
157
+ addMember(member) {
158
+ switch (member.type) {
159
+ // super relation, need to do combination
160
+ case 'relation':
161
+ let binder = new utils_2.LateBinder(this.relations, function (id) {
162
+ const relation = this.refElems.get(`relation/${id}`);
163
+ if (relation) {
164
+ relation.refCount++;
165
+ return relation;
166
+ }
167
+ }, this, [member.ref]);
168
+ this.relations.push(binder);
169
+ this.refElems.addBinder(binder);
170
+ break;
171
+ case 'way':
172
+ if (!member.role) {
173
+ member.role = '';
174
+ }
175
+ let ways = this[member.role];
176
+ if (!ways) {
177
+ ways = this[member.role] = [];
178
+ }
179
+ if (member.geometry) {
180
+ const way = new Way(member.ref, this.refElems);
181
+ way.setLatLngArray(member.geometry);
182
+ way.refCount++;
183
+ ways.push(way);
184
+ }
185
+ else if (member.nodes) {
186
+ const way = new Way(member.ref, this.refElems);
187
+ for (const nid of member.nodes) {
188
+ way.addNodeRef(nid);
189
+ }
190
+ way.refCount++;
191
+ ways.push(way);
192
+ }
193
+ else {
194
+ binder = new utils_2.LateBinder(ways, function (nid) {
195
+ const way = this.refElems.get(`way/${nid}`);
196
+ if (way) {
197
+ way.refCount++;
198
+ return way;
199
+ }
200
+ }, this, [member.ref]);
201
+ ways.push(binder);
202
+ this.refElems.addBinder(binder);
203
+ }
204
+ break;
205
+ case 'node':
206
+ let node = null;
207
+ if (member.lat && member.lon) {
208
+ node = new Node(member.ref, this.refElems);
209
+ node.setLatLng({ lon: member.lon, lat: member.lat });
210
+ if (member.tags) {
211
+ node.addTags(member.tags);
212
+ }
213
+ for (const [k, v] of Object.entries(member)) {
214
+ if (['id', 'type', 'lat', 'lon'].indexOf(k) < 0) {
215
+ node.addProp(k, v);
216
+ }
217
+ }
218
+ node.refCount++;
219
+ this.nodes.push(node);
220
+ }
221
+ else {
222
+ binder = new utils_2.LateBinder(this.nodes, function (id) {
223
+ const nn = this.refElems.get(`node/${id}`);
224
+ if (nn) {
225
+ nn.refCount++;
226
+ return nn;
227
+ }
228
+ }, this, [member.ref]);
229
+ this.nodes.push(binder);
230
+ this.refElems.addBinder(binder);
231
+ }
232
+ default:
233
+ break;
234
+ }
235
+ }
236
+ toFeatureArray() {
237
+ function constructStringGeometry(ws) {
238
+ const strings = ws ? ws.toStrings() : [];
239
+ if (strings.length > 0) {
240
+ if (strings.length === 1) {
241
+ return {
242
+ type: 'LineString',
243
+ coordinates: strings[0],
244
+ };
245
+ }
246
+ return {
247
+ type: 'MultiLineString',
248
+ coordinates: strings,
249
+ };
250
+ }
251
+ return null;
252
+ }
253
+ function constructPolygonGeometry(ows, iws) {
254
+ const outerRings = ows ? ows.toRings('counterclockwise') : [];
255
+ const innerRings = iws ? iws.toRings('clockwise') : [];
256
+ if (outerRings.length > 0) {
257
+ const compositPolyons = [];
258
+ let ring;
259
+ for (ring of outerRings) {
260
+ compositPolyons.push([ring]);
261
+ }
262
+ // link inner polygons to outer containers
263
+ ring = innerRings.shift();
264
+ while (ring) {
265
+ for (const idx in outerRings) {
266
+ if (utils_1.ptInsidePolygon(utils_1.first(ring), outerRings[idx])) {
267
+ compositPolyons[idx].push(ring);
268
+ break;
269
+ }
270
+ }
271
+ ring = innerRings.shift();
272
+ }
273
+ // construct the Polygon/MultiPolygon geometry
274
+ if (compositPolyons.length === 1) {
275
+ return {
276
+ type: 'Polygon',
277
+ coordinates: compositPolyons[0],
278
+ };
279
+ }
280
+ return {
281
+ type: 'MultiPolygon',
282
+ coordinates: compositPolyons,
283
+ };
284
+ }
285
+ return null;
286
+ }
287
+ const polygonFeatures = [];
288
+ const stringFeatures = [];
289
+ let pointFeatures = [];
290
+ const waysFieldNames = ['outer', 'inner', ''];
291
+ // need to do combination when there're nested relations
292
+ for (const relation of this.relations) {
293
+ if (relation) {
294
+ for (const fieldName of waysFieldNames) {
295
+ const ways = relation[fieldName];
296
+ if (ways) {
297
+ const thisWays = this[fieldName];
298
+ if (thisWays) {
299
+ [].splice.apply(thisWays, [thisWays.length, 0].concat(ways));
300
+ }
301
+ else {
302
+ this[fieldName] = ways;
303
+ }
304
+ }
305
+ }
306
+ }
307
+ }
308
+ for (const fieldName of waysFieldNames) {
309
+ const ways = this[fieldName];
310
+ if (ways) {
311
+ this[fieldName] = new utils_2.WayCollection();
312
+ for (const way of ways) {
313
+ this[fieldName].addWay(way);
314
+ }
315
+ }
316
+ }
317
+ let geometry = null;
318
+ const feature = {
319
+ type: 'Feature',
320
+ id: this.getCompositeId(),
321
+ bbox: this.bounds,
322
+ properties: this.getProps(),
323
+ geometry: null,
324
+ };
325
+ if (!this.bounds) {
326
+ delete feature.bbox;
327
+ }
328
+ if (this.outer) {
329
+ geometry = constructPolygonGeometry(this.outer, this.inner);
330
+ if (geometry) {
331
+ feature.geometry = geometry;
332
+ polygonFeatures.push(feature);
333
+ }
334
+ }
335
+ else if (this['']) {
336
+ geometry = constructStringGeometry(this['']);
337
+ if (geometry) {
338
+ feature.geometry = geometry;
339
+ stringFeatures.push(feature);
340
+ }
341
+ }
342
+ for (const node of this.nodes) {
343
+ pointFeatures = pointFeatures.concat(node.toFeatureArray());
344
+ }
345
+ return [...polygonFeatures, ...stringFeatures, ...pointFeatures];
346
+ }
347
+ }
348
+ exports.Relation = Relation;
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function purgeProps(obj, blacklist) {
4
+ if (obj) {
5
+ const rs = Object.assign({}, obj);
6
+ if (blacklist) {
7
+ for (const prop of blacklist) {
8
+ delete rs[prop];
9
+ }
10
+ }
11
+ return rs;
12
+ }
13
+ return {};
14
+ }
15
+ exports.purgeProps = purgeProps;
16
+ function mergeProps(obj1, obj2) {
17
+ obj1 = obj1 ? obj1 : {};
18
+ obj2 = obj2 ? obj2 : {};
19
+ return Object.assign(obj1, obj2);
20
+ }
21
+ exports.mergeProps = mergeProps;
22
+ function addPropToFeature(f, k, v) {
23
+ if (f.properties && k && v) {
24
+ f.properties[k] = v;
25
+ }
26
+ }
27
+ exports.addPropToFeature = addPropToFeature;
28
+ function addPropToFeatures(fs, k, v) {
29
+ for (const f of fs) {
30
+ addPropToFeature(f, k, v);
31
+ }
32
+ }
33
+ exports.addPropToFeatures = addPropToFeatures;
34
+ exports.first = (a) => a[0];
35
+ exports.last = (a) => a[a.length - 1];
36
+ exports.coordsToKey = (a) => a.join(',');
37
+ function addToMap(m, k, v) {
38
+ const a = m[k];
39
+ if (a) {
40
+ a.push(v);
41
+ }
42
+ else {
43
+ m[k] = [v];
44
+ }
45
+ }
46
+ exports.addToMap = addToMap;
47
+ function removeFromMap(m, k, v) {
48
+ const a = m[k];
49
+ let idx = -1;
50
+ if (a) {
51
+ idx = a.indexOf(v);
52
+ }
53
+ if (idx >= 0) {
54
+ a.splice(idx, 1);
55
+ }
56
+ }
57
+ exports.removeFromMap = removeFromMap;
58
+ function getFirstFromMap(m, k) {
59
+ const a = m[k];
60
+ if (a && a.length > 0) {
61
+ return a[0];
62
+ }
63
+ return undefined;
64
+ }
65
+ exports.getFirstFromMap = getFirstFromMap;
66
+ // need 3+ different points to form a ring, here using > 3 is 'coz a the first and the last points are actually the same
67
+ exports.isRing = (a) => a.length > 3 && exports.coordsToKey(exports.first(a)) === exports.coordsToKey(exports.last(a));
68
+ exports.ringDirection = (a, xIdx, yIdx) => {
69
+ xIdx = xIdx || 0, yIdx = yIdx || 1;
70
+ // get the index of the point which has the maximum x value
71
+ const m = a.reduce((maxxIdx, v, idx) => a[maxxIdx][xIdx || 0] > v[xIdx || 0] ? maxxIdx : idx, 0);
72
+ // 'coz the first point is virtually the same one as the last point,
73
+ // we need to skip a.length - 1 for left when m = 0,
74
+ // and skip 0 for right when m = a.length - 1;
75
+ const l = m <= 0 ? a.length - 2 : m - 1;
76
+ const r = m >= a.length - 1 ? 1 : m + 1;
77
+ const xa = a[l][xIdx];
78
+ const xb = a[m][xIdx];
79
+ const xc = a[r][xIdx];
80
+ const ya = a[l][yIdx];
81
+ const yb = a[m][yIdx];
82
+ const yc = a[r][yIdx];
83
+ const det = (xb - xa) * (yc - ya) - (xc - xa) * (yb - ya);
84
+ return det < 0 ? 'clockwise' : 'counterclockwise';
85
+ };
86
+ exports.ptInsidePolygon = (pt, polygon, xIdx, yIdx) => {
87
+ xIdx = xIdx || 0, yIdx = yIdx || 1;
88
+ let result = false;
89
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
90
+ if ((polygon[i][xIdx] <= pt[xIdx] && pt[xIdx] < polygon[j][xIdx] ||
91
+ polygon[j][xIdx] <= pt[xIdx] && pt[xIdx] < polygon[i][xIdx]) &&
92
+ pt[yIdx] < (polygon[j][yIdx] - polygon[i][yIdx]) * (pt[xIdx] - polygon[i][xIdx]) / (polygon[j][xIdx] - polygon[i][xIdx]) + polygon[i][yIdx]) {
93
+ result = !result;
94
+ }
95
+ }
96
+ return result;
97
+ };
98
+ exports.strToFloat = (el) => el instanceof Array ? el.map(exports.strToFloat) : parseFloat(el);
99
+ class LateBinder {
100
+ constructor(container, valueFunc, ctx, args) {
101
+ this.container = container;
102
+ this.valueFunc = valueFunc;
103
+ this.ctx = ctx;
104
+ this.args = args;
105
+ }
106
+ bind() {
107
+ const v = this.valueFunc.apply(this.ctx, this.args);
108
+ if (this.container instanceof Array) {
109
+ const idx = this.container.indexOf(this);
110
+ if (idx >= 0) {
111
+ const args = [idx, 1];
112
+ if (v) {
113
+ args.push(v);
114
+ }
115
+ [].splice.apply(this.container, args);
116
+ }
117
+ }
118
+ else if (typeof this.container === 'object') {
119
+ const k = Object.keys(this.container).find((nv) => this.container[nv] === this);
120
+ if (k) {
121
+ if (v) {
122
+ this.container[k] = v;
123
+ }
124
+ else {
125
+ delete this.container[k];
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
131
+ exports.LateBinder = LateBinder;
132
+ class RefElements extends Map {
133
+ constructor() {
134
+ super();
135
+ this.binders = [];
136
+ }
137
+ add(k, v) {
138
+ this.set(k, v);
139
+ }
140
+ addBinder(binder) {
141
+ this.binders.push(binder);
142
+ }
143
+ bindAll() {
144
+ this.binders.forEach((binder) => binder.bind());
145
+ }
146
+ }
147
+ exports.RefElements = RefElements;
148
+ class WayCollection extends Array {
149
+ constructor() {
150
+ super();
151
+ this.firstMap = {};
152
+ this.lastMap = {};
153
+ }
154
+ addWay(way) {
155
+ const w = way.toCoordsArray();
156
+ if (w.length > 0) {
157
+ this.push(way);
158
+ addToMap(this.firstMap, exports.coordsToKey(exports.first(w)), w);
159
+ addToMap(this.lastMap, exports.coordsToKey(exports.last(w)), w);
160
+ }
161
+ }
162
+ toStrings() {
163
+ const strings = [];
164
+ let way = this.shift();
165
+ while (way) {
166
+ removeFromMap(this.firstMap, exports.coordsToKey(exports.first(way)), way);
167
+ removeFromMap(this.lastMap, exports.coordsToKey(exports.last(way)), way);
168
+ let current = way;
169
+ let next;
170
+ do {
171
+ const key = exports.coordsToKey(exports.last(current));
172
+ let shouldReverse = false;
173
+ next = getFirstFromMap(this.firstMap, key);
174
+ if (!next) {
175
+ next = getFirstFromMap(this.lastMap, key);
176
+ shouldReverse = true;
177
+ }
178
+ if (next) {
179
+ this.splice(this.indexOf(next), 1);
180
+ removeFromMap(this.firstMap, exports.coordsToKey(exports.first(next)), next);
181
+ removeFromMap(this.lastMap, exports.coordsToKey(exports.last(next)), next);
182
+ if (shouldReverse) {
183
+ // always reverse shorter one to save time
184
+ if (next.length > current.length) {
185
+ [current, next] = [next, current];
186
+ }
187
+ next.reverse();
188
+ }
189
+ current = current.concat(next.slice(1));
190
+ }
191
+ } while (next);
192
+ strings.push(exports.strToFloat(current));
193
+ way = this.shift();
194
+ }
195
+ return strings;
196
+ }
197
+ toRings(direction) {
198
+ const strings = this.toStrings();
199
+ const rings = [];
200
+ let str = strings.shift();
201
+ while (str) {
202
+ if (exports.isRing(str)) {
203
+ if (exports.ringDirection(str) !== direction) {
204
+ str.reverse();
205
+ }
206
+ rings.push(str);
207
+ }
208
+ str = strings.shift();
209
+ }
210
+ return rings;
211
+ }
212
+ }
213
+ exports.WayCollection = WayCollection;