UNPKG

10.9 kBJavaScriptView Raw
1var util = require('util');
2var stream = require('stream');
3var RGBColor = require('rgbcolor');
4var expat = require('node-expat');
5function getRGB(osmColor) {
6 if (osmColor) {
7 var hexRGB = /^#?([a-fA-F\d]{2})([a-fA-F\d]{2})([a-fA-F\d]{2})$/i.exec(osmColor);
8 if (hexRGB) {
9 return "rgb("
10 + (parseInt(hexRGB[1], 16)) + ","
11 + (parseInt(hexRGB[2], 16)) + ","
12 + (parseInt(hexRGB[3], 16)) + ")";
13 }
14 var color = new RGBColor(osmColor);
15 if (color.ok) {
16 return color.toRGB();
17 }
18 }
19 return "rgb(0,0,0)";
20}
21
22var geoBlds = {};
23function heightToMeter(height) {
24 "use strict";
25 var result = 1,
26 unit = height.substr(height.length - 1);
27 switch (unit) {
28 case 'm':
29 result = height.substr(0, height.length - 1);
30 break;
31 default:
32 result = height;
33 break;
34 }
35 return result;
36}
37function getGeoBuilding(id) {
38 "use strict";
39 if (!(geoBlds.hasOwnProperty(id))) {
40 var geoBld = {
41 "type": "FeatureCollection",
42 "features": [],
43 properties: {
44 "id": id,
45 "type": "building"
46 }
47 };
48 geoBlds[id] = geoBld;
49 }
50 return geoBlds[id];
51}
52
53function removeBuilding(id) {
54 "use strict";
55 if (geoBlds.hasOwnProperty(id)) {
56 delete geoBlds[id];
57 }
58}
59
60function createGroundBlock(minlat, minlon, maxlat, maxlon, url) {
61 "use strict";
62 var bounds = {
63 'type': 'Feature',
64 'geometry': {
65 'type': 'Polygon',
66 'coordinates': [[
67 [+minlon, +minlat], [+maxlon, +minlat],
68 [+maxlon, +maxlat], [+minlon, +maxlat]]]
69 },
70 'properties': {
71 'type': 'bounds'
72 }
73 };
74 if (url) {
75// 'url': 'http://a.tile.openstreetmap.org/' + zoom + '/' + x + '/' + y + '.png',
76 bounds['properties']['tile'] = url;
77 }
78 return bounds;
79}
80
81var geoGroundBlock = {
82 "type": "Feature",
83 "geometry": {
84 "type": "Polygon",
85 "coordinates": [[[-73.9860386, 40.7487894], [-73.9860386, 40.7487894], [-73.9860386, 40.7487894], [-73.9860386, 40.7487894]]]
86 },
87 "properties": {
88 "tile": 'http://a.tile.openstreetmap.org/zoom/x/y.png',
89 }
90};
91var geoBldPart = {
92 "type": "Feature",
93 "geometry": {
94 "type": "Polygon",
95 "coordinates": []
96 },
97 "properties": {
98 "type": "buildingPart",
99 "minHeight": 20,
100 "minLevel": 5,
101 "color": "rgb(223, 55, 54)",
102 "levels": 20,
103 "height": 75
104 }
105};
106var geoRoof = {
107 "type": "Feature",
108 "geometry": {
109 "type": "Polygon",
110 "coordinates": []
111 },
112 "properties": {
113 "type": "geoRoof",
114 "color": "rgb(221, 123, 56)",
115 "roofShape": "flat",
116 "maxHeight": 95
117 }
118};
119var geoBld = {
120 "type": "FeatureCollection",
121 "features": [geoBldPart, geoRoof],
122 "properties": {
123 "id": "2098969",
124 "type": "building",
125 "name": "Empire State Building"
126 }
127};
128function convert(options, onConvert) {
129 "use strict";
130 var xmlStream = new expat.Parser('UTF-8');
131 xmlStream.on('error', function (error) {
132 console.error("error!", error);
133 })
134 var blocks = [],
135 geoBldParts = {},
136 geoRoofs = {},
137 nodeMap = {},
138 onWay = false,
139 onRelation = false;
140 var way = {};
141 var relation = {};
142 xmlStream.on('startElement', function (name, attributes) {
143 if (name === 'bounds') {
144 var bounds = createGroundBlock(
145 +attributes.minlat, +attributes.minlon,
146 +attributes.maxlat, +attributes.maxlon,
147 ((options && options.tile) ? options.tile : null));
148// console.log(JSON.stringify(bounds));
149 blocks[blocks.length] = bounds;
150 } else if (name === 'node') {
151 var id = attributes.id;
152 var lat = attributes.lat;
153 var lon = attributes.lon;
154 nodeMap[id] = [+lon, +lat];
155 } else if (name === 'way') {
156 way.nodes = [];
157 way.id = attributes.id;
158 way.isBld = false;
159 way.name = undefined;
160 way.color = undefined;
161 way.osmBldPartHeight = undefined;
162 way.minHeight = 0;
163 way.bldLevels = undefined;
164 way.bldMinLevel = 0;
165 way.roofHeight = undefined;
166 way.roofShape = "flat";
167 onWay = true;
168 } else if (name === 'nd' && onWay) {
169 if (!nodeMap.hasOwnProperty(attributes.ref)) {
170 console.log("ERROR: Cannot link way to this node: " + attributes.ref);
171 } else {
172 way.nodes[way.nodes.length] = nodeMap[attributes.ref];
173 }
174 } else if (name === 'tag' && onWay) {
175 if (attributes.k === 'building') {
176 way.isBld = true;
177 } else if (attributes.k === 'building:levels') {
178 way.bldLevels = attributes.v;
179 } else if (attributes.k === 'building:min_level') {
180 way.bldMinLevel = attributes.v;
181 } else if (attributes.k === 'height') {
182 way.osmBldPartHeight = heightToMeter(attributes.v);
183 } else if (attributes.k === 'min_height') {
184 way.minHeight = heightToMeter(attributes.v);
185 } else if (attributes.k === 'name') {
186 way.name = attributes.v;
187 } else if (attributes.k === 'building:colour') {
188 way.color = attributes.v;
189 } else if (attributes.k === 'roof:shape') {
190 way.roofShape = attributes.v;
191 } else if (attributes.k === 'roof:height') {
192 way.roofHeight = attributes.v;
193 }
194 } else if (name === 'relation') {
195 relation.id = attributes.id;
196 relation.isBld = false;
197 relation.osmBldPartHeight = undefined;
198 relation.minHeight = undefined;
199 relation.name = undefined;
200 relation.pptRoofHeight = undefined;
201 relation.roofShape = 'flat';
202 onRelation = true;
203 } else if (onRelation && name === 'member' && attributes.type === 'way') {
204 if (attributes.ref in geoBldParts) {
205 var geoBld = getGeoBuilding(relation.id);
206 geoBld.features[geoBld.features.length] = geoBldParts[attributes.ref];
207 if (geoRoofs[attributes.ref]) {
208 geoBld.features[geoBld.features.length] = geoRoofs[attributes.ref];
209 }
210 }
211 } else if (onRelation && name === 'tag' && attributes.k === 'name') {
212 relation.name = attributes.v;
213 } else if (onRelation && name === 'tag' && attributes.k === 'building:part' && attributes.v === 'yes') {
214 relation.isBld = true;
215 } else if (onRelation && name === 'tag' && attributes.k === 'building' && attributes.v === 'yes') {
216 relation.isBld = true;
217 } else if (onRelation && name === 'tag' && attributes.k === 'type' && attributes.v === 'building') {
218 relation.isBld = true;
219 } else if (onRelation && name === 'tag' && attributes.k === 'height') {
220 relation.osmBldPartHeight = heightToMeter(attributes.v);
221 } else if (onRelation && name === 'tag' && attributes.k === 'min_height') {
222 relation.minHeight = heightToMeter(attributes.v);
223 } else if (onRelation && name === 'tag' && attributes.k === 'roof:shape') {
224 relation.roofShape = attributes.v;
225 } else if (onRelation && name === 'tag' && attributes.k === 'roof:height') {
226 relation.optRoofHeight = attributes.v;
227 }
228 });
229 xmlStream.on('endElement', function (name) {
230 if (name === 'way') {
231 onWay = false;
232 if (way.roofShape) {
233 var geoRoof = {
234 "type": "Feature",
235 "geometry": {
236 "type": "Polygon",
237 "coordinates": [way.nodes]
238 },
239 "properties": {
240 "id": way.id,
241 "type": "geoRoof",
242 "color": getRGB(way.color),
243 "roofShape": way.roofShape,
244 "height": way.osmBldPartHeight
245 }
246 };
247 }
248 var geoBldPart = {
249 "type": "Feature",
250 "geometry": {
251 "type": "Polygon",
252 "coordinates": [way.nodes]
253 },
254 "properties": {
255 "id": way.id,
256 "type": "buildingPart",
257 "minHeight": way.minHeight,
258 "minLevel": way.bldMinLevel,
259 "name": way.name,
260 "color": getRGB(way.color),
261 "levels": way.bldLevels,
262 "height": way.osmBldPartHeight
263 }
264 };
265 if (way.isBld) {
266 var geoBld = getGeoBuilding(way.id);
267 geoBld.features[geoBld.features.length] = geoBldPart;
268 if (geoRoof) {
269 geoBld.features[geoBld.features.length] = geoRoof;
270 }
271 blocks[blocks.length] = JSON.parse(JSON.stringify(geoBld));
272 } else {
273 geoBldParts[way.id] = geoBldPart;
274 if (geoRoof) {
275 geoRoofs[way.id] = geoRoof;
276 }
277 }
278 } else if (name === 'relation') {
279 if (!relation.isBld) {
280 removeBuilding(relation.id);
281 } else {
282 var geoBld = getGeoBuilding(relation.id);
283 if (relation.osmBldPartHeight !== undefined) {
284 geoBld.properties.name = relation.name;
285 for (var i = 0; i < geoBld.features.length; i++) {
286 var geoBldPart = geoBld.features[i];
287 if (geoBldPart.properties.height !== undefined) {
288 geoBldPart.properties.height = relation.osmBldPartHeight;
289 }
290 if (geoBldPart.properties.minHeight === undefined) {
291 geoBldPart.properties.minHeight = relation.minHeight;
292 }
293 }
294 }
295 blocks[blocks.length] = JSON.parse(JSON.stringify(geoBld));
296 }
297 onRelation = false;
298 } else if (name === 'osm') {
299// if (onConvert !== undefined) {
300 onConvert(blocks);
301// }
302 // tile ground
303
304 }
305 });
306 return xmlStream;
307}
308
309exports.convert = convert;