1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | import theConfig from '../../data/Config.js'; |
26 | import theGeometry from '../../core/lib/Geometry.js'; |
27 | import { SVG_NS, ICON_DIMENSIONS, ZERO, ONE, TWO, NOT_FOUND } from '../../main/Constants.js'; |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | class SvgBuilder { |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | #computeData; |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 | #overpassAPIDataLoader; |
50 | |
51 | |
52 | |
53 | |
54 | |
55 | |
56 | #svgElement; |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | #createSvg ( ) { |
63 | |
64 | const FOUR = 4; |
65 | this.#svgElement = document.createElementNS ( SVG_NS, 'svg' ); |
66 | this.#svgElement.setAttributeNS ( |
67 | null, |
68 | 'viewBox', |
69 | String ( ICON_DIMENSIONS.svgViewboxDim / FOUR ) + ' ' + |
70 | ( ICON_DIMENSIONS.svgViewboxDim / FOUR ) + ' ' + |
71 | ( ICON_DIMENSIONS.svgViewboxDim / TWO ) + ' ' + |
72 | ( ICON_DIMENSIONS.svgViewboxDim / TWO ) |
73 | ); |
74 | this.#svgElement.setAttributeNS ( null, 'class', 'TravelNotes-SvgIcon' ); |
75 | } |
76 | |
77 | |
78 | |
79 | |
80 | |
81 | #createRoute ( ) { |
82 | |
83 | |
84 | let index = -ONE; |
85 | let firstPointIndex = NOT_FOUND; |
86 | let lastPointIndex = NOT_FOUND; |
87 | const points = []; |
88 | this.#computeData.route.itinerary.itineraryPoints.forEach ( |
89 | itineraryPoint => { |
90 | index ++; |
91 | const point = theGeometry.addPoints ( |
92 | theGeometry.project ( itineraryPoint.latLng, theConfig.note.svgIcon.zoom ), |
93 | this.#computeData.translation |
94 | ); |
95 | points.push ( point ); |
96 | const pointIsInside = |
97 | point [ ZERO ] >= ZERO |
98 | && |
99 | point [ ONE ] >= ZERO |
100 | && |
101 | point [ ZERO ] <= ICON_DIMENSIONS.svgViewboxDim |
102 | && |
103 | point [ ONE ] <= ICON_DIMENSIONS.svgViewboxDim; |
104 | if ( pointIsInside ) { |
105 | if ( NOT_FOUND === firstPointIndex ) { |
106 | firstPointIndex = index; |
107 | } |
108 | lastPointIndex = index; |
109 | } |
110 | } |
111 | ); |
112 | if ( NOT_FOUND !== firstPointIndex && NOT_FOUND !== lastPointIndex ) { |
113 | if ( ZERO < firstPointIndex ) { |
114 | firstPointIndex --; |
115 | } |
116 | if ( this.#computeData.route.itinerary.itineraryPoints.length - ONE > lastPointIndex ) { |
117 | lastPointIndex ++; |
118 | } |
119 | let pointsAttribute = ''; |
120 | for ( index = firstPointIndex; index <= lastPointIndex; index ++ ) { |
121 | pointsAttribute += points[ index ] [ ZERO ].toFixed ( ZERO ) + ',' + |
122 | points[ index ] [ ONE ].toFixed ( ZERO ) + ' '; |
123 | } |
124 | const polyline = document.createElementNS ( SVG_NS, 'polyline' ); |
125 | polyline.setAttributeNS ( null, 'points', pointsAttribute ); |
126 | polyline.setAttributeNS ( null, 'class', 'TravelNotes-OSM-Itinerary' ); |
127 | polyline.setAttributeNS ( |
128 | null, |
129 | 'transform', |
130 | 'rotate(' + this.#computeData.rotation + |
131 | ',' + ( ICON_DIMENSIONS.svgViewboxDim / TWO ) + |
132 | ',' + ( ICON_DIMENSIONS.svgViewboxDim / TWO ) |
133 | + ')' |
134 | ); |
135 | this.#svgElement.appendChild ( polyline ); |
136 | } |
137 | } |
138 | |
139 | |
140 | |
141 | |
142 | |
143 | #createWays ( ) { |
144 | |
145 | |
146 | this.#overpassAPIDataLoader.ways.forEach ( |
147 | way => { |
148 | let firstPointIndex = NOT_FOUND; |
149 | let lastPointIndex = NOT_FOUND; |
150 | let index = -ONE; |
151 | const points = [ ]; |
152 | way.nodes.forEach ( |
153 | nodeId => { |
154 | index ++; |
155 | const node = this.#overpassAPIDataLoader.nodes.get ( nodeId ); |
156 | const point = theGeometry.addPoints ( |
157 | theGeometry.project ( [ node.lat, node.lon ], theConfig.note.svgIcon.zoom ), |
158 | this.#computeData.translation |
159 | ); |
160 | points.push ( point ); |
161 | const pointIsInside = |
162 | point [ ZERO ] >= ZERO |
163 | && |
164 | point [ ONE ] >= ZERO |
165 | && |
166 | point [ ZERO ] <= ICON_DIMENSIONS.svgViewboxDim |
167 | && |
168 | point [ ONE ] <= ICON_DIMENSIONS.svgViewboxDim; |
169 | if ( pointIsInside ) { |
170 | if ( NOT_FOUND === firstPointIndex ) { |
171 | firstPointIndex = index; |
172 | } |
173 | lastPointIndex = index; |
174 | } |
175 | } |
176 | ); |
177 | if ( NOT_FOUND !== firstPointIndex && NOT_FOUND !== lastPointIndex ) { |
178 | if ( ZERO < firstPointIndex ) { |
179 | firstPointIndex --; |
180 | } |
181 | if ( way.nodes.length - ONE > lastPointIndex ) { |
182 | lastPointIndex ++; |
183 | } |
184 | let pointsAttribute = ''; |
185 | for ( index = firstPointIndex; index <= lastPointIndex; index ++ ) { |
186 | pointsAttribute += |
187 | points[ index ] [ ZERO ].toFixed ( ZERO ) + ',' + |
188 | points[ index ] [ ONE ].toFixed ( ZERO ) + ' '; |
189 | } |
190 | |
191 | const polyline = document.createElementNS ( SVG_NS, 'polyline' ); |
192 | polyline.setAttributeNS ( null, 'points', pointsAttribute ); |
193 | polyline.setAttributeNS ( |
194 | null, |
195 | 'class', |
196 | 'TravelNotes-OSM-Highway TravelNotes-OSM-Highway-' + way.tags.highway |
197 | ); |
198 | polyline.setAttributeNS ( |
199 | null, |
200 | 'transform', |
201 | 'rotate(' + this.#computeData.rotation + |
202 | ',' + ( ICON_DIMENSIONS.svgViewboxDim / TWO ) + |
203 | ',' + ( ICON_DIMENSIONS.svgViewboxDim / TWO ) + |
204 | ')' |
205 | ); |
206 | |
207 | this.#svgElement.appendChild ( polyline ); |
208 | } |
209 | } |
210 | ); |
211 | } |
212 | |
213 | |
214 | |
215 | |
216 | |
217 | #createRcnRef ( ) { |
218 | |
219 | if ( '' === this.#computeData.rcnRef ) { |
220 | return; |
221 | } |
222 | const svgText = document.createElementNS ( SVG_NS, 'text' ); |
223 | svgText.textContent = this.#computeData.rcnRef; |
224 | svgText.setAttributeNS ( null, 'x', String ( ICON_DIMENSIONS.svgViewboxDim / TWO ) ); |
225 | svgText.setAttributeNS ( null, 'y', String ( ICON_DIMENSIONS.svgViewboxDim / TWO ) ); |
226 | svgText.setAttributeNS ( null, 'class', 'TravelNotes-OSM-RcnRef' ); |
227 | this.#svgElement.appendChild ( svgText ); |
228 | } |
229 | |
230 | |
231 | |
232 | |
233 | |
234 | constructor ( ) { |
235 | Object.freeze ( this ); |
236 | } |
237 | |
238 | |
239 | |
240 | |
241 | |
242 | |
243 | |
244 | |
245 | buildSvg ( computeData, noteData, overpassAPIDataLoader ) { |
246 | |
247 | this.#computeData = computeData; |
248 | this.#overpassAPIDataLoader = overpassAPIDataLoader; |
249 | |
250 | this.#createSvg ( ); |
251 | this.#createRoute ( ); |
252 | this.#createWays ( ); |
253 | this.#createRcnRef ( ); |
254 | |
255 | noteData.iconContent = this.#svgElement.outerHTML; |
256 | } |
257 | } |
258 | |
259 | export default SvgBuilder; |
260 | |
261 | |
262 | |