1+ import { apis } from './apis.js' ;
12import { BuildingShapeUtils } from './extras/BuildingShapeUtils.js' ;
23import { BuildingPart } from './buildingpart.js' ;
34import { MultiBuildingPart } from './multibuildingpart.js' ;
@@ -8,16 +9,28 @@ import {MultiBuildingPart} from './multibuildingpart.js';
89 * XML data from the API.
910 */
1011class Building {
11- // Latitude and longitude that transitioned to (0, 0)
12+ /**
13+ * Latitude and longitude that transitions to (0, 0)
14+ * @type {number[2] }
15+ */
1216 home = [ ] ;
1317
14- // the parts
18+ /**
19+ * The parts.
20+ * @type {BuildingPart[] }
21+ */
1522 parts = [ ] ;
1623
17- // the BuildingPart of the outer building parimeter
24+ /**
25+ * The building part of the outer parimeter.
26+ * @type {BuildingPart }
27+ */
1828 outerElement ;
1929
20- // DOM Tree of all elements to render
30+ /**
31+ * DOM Tree of all elements to render
32+ * @type {DOM.Element }
33+ */
2134 fullXmlData ;
2235
2336 id = '0' ;
@@ -29,21 +42,38 @@ class Building {
2942 type ;
3043 options ;
3144
45+ static async getRelationDataWithChildRelations ( id ) {
46+ const xmlData = new window . DOMParser ( ) . parseFromString ( await Building . getRelationData ( id ) , 'text/xml' ) ;
47+ await Promise . all ( Array . from ( xmlData . querySelectorAll ( 'member[type=relation]' ) ) . map ( async r => {
48+ const childId = r . getAttribute ( 'ref' ) ;
49+ if ( r . getAttribute ( 'id' ) === childId ) {
50+ return ;
51+ }
52+ const childData = new window . DOMParser ( ) . parseFromString ( await Building . getRelationData ( childId ) , 'text/xml' ) ;
53+ childData . querySelectorAll ( 'node, way, relation' ) . forEach ( i => {
54+ if ( xmlData . querySelector ( `${ i . tagName } [id="${ i . getAttribute ( 'id' ) } "]` ) ) {
55+ return ;
56+ }
57+ xmlData . querySelector ( 'osm' ) . appendChild ( i ) ;
58+ } ) ;
59+ } ) ) ;
60+ return new XMLSerializer ( ) . serializeToString ( xmlData ) ;
61+ }
62+
3263 /**
33- * Create new building
64+ * Download data for new building
3465 */
35- static async create ( type , id ) {
36- var data ;
66+ static async downloadDataAroundBuilding ( type , id ) {
67+ let data ;
3768 if ( type === 'way' ) {
3869 data = await Building . getWayData ( id ) ;
3970 } else {
40- data = await Building . getRelationData ( id ) ;
71+ data = await Building . getRelationDataWithChildRelations ( id ) ;
4172 }
4273 let xmlData = new window . DOMParser ( ) . parseFromString ( data , 'text/xml' ) ;
4374 const nodelist = Building . buildNodeList ( xmlData ) ;
4475 const extents = Building . getExtents ( id , xmlData , nodelist ) ;
45- const innerData = await Building . getInnerData ( ...extents ) ;
46- return new Building ( id , innerData ) ;
76+ return await Building . getInnerData ( ...extents ) ;
4777 }
4878
4979 /**
@@ -63,29 +93,30 @@ class Building {
6393 } else {
6494 this . type = 'relation' ;
6595 }
66- if ( this . isValidData ( outerElementXml ) ) {
67- this . nodelist = Building . buildNodeList ( this . fullXmlData ) ;
68- this . setHome ( ) ;
69- this . repositionNodes ( ) ;
70- if ( this . type === 'way' ) {
96+ try {
97+ this . validateData ( outerElementXml ) ;
98+ } catch ( e ) {
99+ throw new Error ( `Rendering of ${ outerElementXml . tagName . toLowerCase ( ) } ${ id } is not possible. ${ e } ` ) ;
100+ }
101+
102+ this . nodelist = Building . buildNodeList ( this . fullXmlData ) ;
103+ this . setHome ( ) ;
104+ this . repositionNodes ( ) ;
105+ if ( this . type === 'way' ) {
106+ this . outerElement = new BuildingPart ( id , this . fullXmlData , this . nodelist ) ;
107+ } else if ( this . type === 'multipolygon' ) {
108+ this . outerElement = new MultiBuildingPart ( id , this . fullXmlData , this . nodelist ) ;
109+ } else {
110+ const outlineRef = outerElementXml . querySelector ( 'member[role="outline"]' ) . getAttribute ( 'ref' ) ;
111+ const outline = this . fullXmlData . getElementById ( outlineRef ) ;
112+ const outlineType = outline . tagName . toLowerCase ( ) ;
113+ if ( outlineType === 'way' ) {
71114 this . outerElement = new BuildingPart ( id , this . fullXmlData , this . nodelist ) ;
72- } else if ( this . type === 'multipolygon' ) {
73- this . outerElement = new MultiBuildingPart ( id , this . fullXmlData , this . nodelist ) ;
74115 } else {
75- const outlineRef = outerElementXml . querySelector ( 'member[role="outline"]' ) . getAttribute ( 'ref' ) ;
76- const outline = this . fullXmlData . getElementById ( outlineRef ) ;
77- const outlineType = outline . tagName . toLowerCase ( ) ;
78- if ( outlineType === 'way' ) {
79- this . outerElement = new BuildingPart ( id , this . fullXmlData , this . nodelist ) ;
80- } else {
81- this . outerElement = new MultiBuildingPart ( outlineRef , this . fullXmlData , this . nodelist ) ;
82- }
116+ this . outerElement = new MultiBuildingPart ( outlineRef , this . fullXmlData , this . nodelist ) ;
83117 }
84- this . addParts ( ) ;
85- } else {
86- window . printError ( 'XML Not Valid' ) ;
87- throw new Error ( 'invalid XML' ) ;
88118 }
119+ this . addParts ( ) ;
89120 }
90121
91122 /**
@@ -140,7 +171,12 @@ class Building {
140171 const mesh = [ ] ;
141172 if ( this . parts . length > 0 ) {
142173 this . outerElement . options . building . visible = false ;
143- mesh . push ( ...this . outerElement . render ( ) ) ;
174+ const outerMeshes = this . outerElement . render ( ) ;
175+ outerMeshes [ 0 ] . visible = false ;
176+ this . outerElement . options . roof . visible = false ;
177+ outerMeshes [ 1 ] . visible = false ;
178+ this . outerElement . options . building . visible = false ;
179+ mesh . push ( ...outerMeshes ) ;
144180 for ( let i = 0 ; i < this . parts . length ; i ++ ) {
145181 mesh . push ( ...this . parts [ i ] . render ( ) ) ;
146182 }
@@ -181,7 +217,11 @@ class Building {
181217 for ( let i = 0 ; i < parts . length ; i ++ ) {
182218 if ( parts [ i ] . querySelector ( '[k="building:part"]' ) ) {
183219 const id = parts [ i ] . getAttribute ( 'id' ) ;
184- this . parts . push ( new MultiBuildingPart ( id , this . fullXmlData , this . nodelist , this . outerElement . options ) ) ;
220+ try {
221+ this . parts . push ( new MultiBuildingPart ( id , this . fullXmlData , this . nodelist , this . outerElement . options ) ) ;
222+ } catch ( e ) {
223+ window . printError ( e ) ;
224+ }
185225 }
186226 }
187227 }
@@ -193,38 +233,53 @@ class Building {
193233 static async getWayData ( id ) {
194234 let restPath = apis . getWay . url ( id ) ;
195235 let response = await fetch ( restPath ) ;
196- let text = await response . text ( ) ;
197- return text ;
236+ if ( response . status === 404 ) {
237+ throw `The way ${ id } was not found on the server.\nURL: ${ restPath } ` ;
238+ } else if ( response . status === 410 ) {
239+ throw `The way ${ id } was deleted.\nURL: ${ restPath } ` ;
240+ } else if ( response . status !== 200 ) {
241+ throw `HTTP ${ response . status } .\nURL: ${ restPath } ` ;
242+ }
243+ return await response . text ( ) ;
198244 }
199245
200246 static async getRelationData ( id ) {
201247 let restPath = apis . getRelation . url ( id ) ;
202248 let response = await fetch ( restPath ) ;
203- let text = await response . text ( ) ;
204- return text ;
249+ if ( response . status === 404 ) {
250+ throw `The relation ${ id } was not found on the server.\nURL: ${ restPath } ` ;
251+ } else if ( response . status === 410 ) {
252+ throw `The relation ${ id } was deleted.\nURL: ${ restPath } ` ;
253+ } else if ( response . status !== 200 ) {
254+ throw `HTTP ${ response . status } .\nURL: ${ restPath } ` ;
255+ }
256+ return await response . text ( ) ;
205257 }
206258
207259 /**
208- * Fetch way data from OSM
260+ * Fetch map data data from OSM
209261 */
210262 static async getInnerData ( left , bottom , right , top ) {
211- let response = await fetch ( apis . bounding . url ( left , bottom , right , top ) ) ;
212- let res = await response . text ( ) ;
213- return res ;
263+ let url = apis . bounding . url ( left , bottom , right , top ) ;
264+ let response = await fetch ( url ) ;
265+ if ( response . status !== 200 ) {
266+ throw `HTTP ${ response . status } .\nURL: ${ url } ` ;
267+ }
268+ return await response . text ( ) ;
214269 }
215270
216271 /**
217272 * validate that we have the ID of a building way.
218273 */
219- isValidData ( xmlData ) {
274+ validateData ( xmlData ) {
220275 // Check that it is a building (<tag k="building" v="*"/> exists)
221276 const buildingType = xmlData . querySelector ( '[k="building"]' ) ;
222277 const ways = [ ] ;
223278 if ( xmlData . tagName === 'relation' ) {
224279 // get all building relation parts
225280 // todo: multipolygon inner and outer roles.
226281 let parts = xmlData . querySelectorAll ( 'member[role="part"]' ) ;
227- var ref = 0 ;
282+ let ref = 0 ;
228283 for ( let i = 0 ; i < parts . length ; i ++ ) {
229284 ref = parts [ i ] . getAttribute ( 'ref' ) ;
230285 const part = this . fullXmlData . getElementById ( ref ) ;
@@ -236,8 +291,7 @@ class Building {
236291 }
237292 } else {
238293 if ( ! buildingType ) {
239- window . printError ( 'Outer way is not a building' ) ;
240- return false ;
294+ throw new Error ( 'Outer way is not a building' ) ;
241295 }
242296 ways . push ( xmlData ) ;
243297 }
@@ -250,16 +304,14 @@ class Building {
250304 const firstRef = nodes [ 0 ] . getAttribute ( 'ref' ) ;
251305 const lastRef = nodes [ nodes . length - 1 ] . getAttribute ( 'ref' ) ;
252306 if ( firstRef !== lastRef ) {
253- window . printError ( 'Way ' + way . getAttribute ( 'id' ) + ' is not a closed way. ' + firstRef + ' !== ' + lastRef + '.' ) ;
254- return false ;
307+ throw new Error ( 'Way ' + way . getAttribute ( 'id' ) + ' is not a closed way. ' + firstRef + ' !== ' + lastRef + '.' ) ;
255308 }
256309 } else {
257- window . printError ( 'Way ' + way . getAttribute ( 'id' ) + ' has no nodes.' ) ;
258- return false ;
310+ throw new Error ( 'Way ' + way . getAttribute ( 'id' ) + ' has no nodes.' ) ;
259311 }
260312 } else {
261313 let parts = way . querySelectorAll ( 'member[role="part"]' ) ;
262- var ref = 0 ;
314+ let ref = 0 ;
263315 for ( let i = 0 ; i < parts . length ; i ++ ) {
264316 ref = parts [ i ] . getAttribute ( 'ref' ) ;
265317 const part = this . fullXmlData . getElementById ( ref ) ;
0 commit comments