UNPKG

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