| 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 | |