1/** 2 * Mapbox Geocoder Widget 3 * 4 * new WidgetGeoPicker("lnglat", { 5 * "button": "buttonId", 6 * "map": "mapContainerId", 7 * "apikey": "xxxxxxxxx" 8 * }); 9 * 10 * new WidgetGeoPicker(["lng", "lng"], { 11 * "map": "mapContainerId", 12 * "apikey": "xxxxxxxxx" 13 * }); 14 * 15 */ 16class WidgetGeoPicker { 17 constructor (fields, opts) { 18 const defaults = { 19 "button": "", 20 "map": "", 21 "preview": true, 22 "mapboxToken": "pk.eyJ1IjoiYjE1cHJvamVjdHMiLCJhIjoiY2t4ZXFqbHcwMWh3YzJya3lxa2dnMWg2YSJ9.3AzGYYZuT4iN50awlSSLAg" 23 }; 24 this.config = Object.assign({}, defaults, opts); 25 this.eles = this.fieldelement(fields); // [eleLng, eleLat | eleLngLat] 26 this.modal = undefined; 27 this.preview = undefined; 28 this.showpicker = this.showpicker.bind(this); 29 this.removemodal = this.removemodal.bind(this); 30 this.clearselected = this.clearselected.bind(this); 31 this.initmapbox = this.initmapbox.bind(this); 32 this.saveselection = this.saveselection.bind(this); 33 this.location = false; 34 this.init(); 35 } 36 37 /** 38 * Binds events to dom elements 39 * 40 */ 41 init () { 42 if (this.config.button) { 43 this.config.button = document.getElementById(this.config.button); 44 if (this.config.button) { 45 this.config.button.addEventListener("click", this.showpicker); 46 } 47 } else { 48 this.eles.forEach((fld, i) => { 49 fld.addEventListener("focus", this.showpicker); 50 }); 51 } 52 } 53 54 /** 55 * Renders and attaches the GeoPicker modal to the DOM 56 * 57 */ 58 showpicker () { 59 this.removemodal(); 60 this.modal = document.createElement("div"); 61 this.modal.classList.add(this.constructor.name); 62 this.modal.innerHTML = ` 63 <div class="ui container"> 64 <div class="ui grid"> 65 <div class="twelve wide column"></div> 66 <div class="four wide right aligned column"> 67 <div class="ui buttons" style="padding-top:0.6rem;"> 68 <button id="${this.constructor.name}_cancel" class="ui button">Cancel</button> 69 <button id="${this.constructor.name}_save" class="ui primary button">Save</button> 70 </div> 71 </div> 72 <div id="map" class="sixteen wide column"></div> 73 </div> 74 </div> 75 `; 76 document.body.append(this.modal); 77 this.initmapbox(); 78 } 79 80 /** 81 * Invokes the MapBox map and event handlers 82 * 83 */ 84 initmapbox () { 85 const that = this; 86 mapboxgl.accessToken = this.config.mapboxToken; 87 const map = new mapboxgl.Map({ 88 container: 'map', 89 bounds: [[-84.39, 33.757], [-75.38, 36.624]], 90 style: 'mapbox://styles/mapbox/streets-v11', 91 zoom: 6 92 }) 93 .on('click', function (e) { 94 that.location = [e.lngLat.lng, e.lngLat.lat]; 95 updateMarker(that.location); 96 }); 97 const updateMarker = (loc) => { 98 if (map._markers.length > 0) { 99 map._markers[0].setLngLat(loc); 100 return map._markers[0]; 101 } 102 const marker = new mapboxgl.Marker({draggable: true}) 103 .setLngLat(loc).addTo(map); 104 marker.on("dragend", function (e) { 105 const lngLat = this.getLngLat(); 106 that.location = [lngLat.lng, lngLat.lat]; 107 }); 108 return marker; 109 }; 110 const coordinatesGeocoder = function (query) { 111 const matches = query.match(/^[ ]*(?:Lat: )?(-?\d+\.?\d*)[, ]+(?:Lng: )?(-?\d+\.?\d*)[ ]*$/i); 112 if (!matches) { 113 return null; 114 } 115 function coordinateFeature(lng, lat) { 116 return { 117 center: [lng, lat], 118 geometry: { 119 type: 'Point', 120 coordinates: [lng, lat] 121 }, 122 place_name: 'Lat: ' + lat + ' Lng: ' + lng, 123 place_type: ['coordinate'], 124 properties: {}, 125 type: 'Feature' 126 }; 127 } 128 const coord1 = Number(matches[1]); 129 const coord2 = Number(matches[2]); 130 const geocodes = []; 131 if (coord1 < -90 || coord1 > 90) {
132 geocodes.push(coordinateFeature(coord1, coord2)); 133 } 134 if (coord2 < -90 || coord2 > 90) { 135 geocodes.push(coordinateFeature(coord2, coord1)); 136 } 137 if (geocodes.length === 0) { 138 geocodes.push(coordinateFeature(coord1, coord2)); 139 geocodes.push(coordinateFeature(coord2, coord1)); 140 } 141 return geocodes; 142 }; 143 const GeoCoder = new MapboxGeocoder({ 144 accessToken: mapboxgl.accessToken, 145 localGeocoder: coordinatesGeocoder, 146 zoom: 4, 147 placeholder: 'address or lat,lng', 148 mapboxgl: mapboxgl, 149 reverseGeocode: true, 150 marker: false 151 }); 152 GeoCoder.on("result", (rslt) => { 153 this.location = rslt.result.center; 154 updateMarker(this.location); 155 }); 156 // Add the control to the map. 157 map.addControl(GeoCoder, "top-left"); 158 // Widget Buttons 159 const xbut = document.getElementById(`${this.constructor.name}_cancel`); 160 if (xbut) { 161 xbut.addEventListener("click", this.removemodal); 162 } 163 const sbut = document.getElementById(`${this.constructor.name}_save`); 164 if (sbut) { 165 sbut.addEventListener("click", this.saveselection); 166 } 167 } 168 169 /** 170 * Update the form LngLat with the selected values 171 */ 172 saveselection (e) { 173 if (this.location && this.eles) { 174 if (this.eles.length == 1) { 175 this.eles[0].value = this.location.join(","); 176 } else { 177 this.eles[0].value = this.location[0]; 178 this.eles[1].value = this.location[1]; 179 } 180 this.staticmap(); 181 this.location = false; 182 } else { 183 alert("Please set a location to save."); 184 return; 185 } 186 this.removemodal(); 187 } 188 189 /** 190 * Display the selected loction with a static map 191 */ 192 staticmap () { 193 const css = `${this.constructor.name}_preview`; 194 if (this.location && this.config.preview) { 195 const lng = this.location[0]; 196 const lat = this.location[1]; 197 let url = `https://api.mapbox.com/styles/v1/mapbox/streets-v11/static/pin-l+1c71d8(${lng},${lat})/${lng},${lat},13.89,0/600x384@2x?access_token=${this.config.mapboxToken}`; 198 let lnglat = "Lng:" + this.location.join(", Lat:"); 199 if (!this.preview) { 200 this.preview = document.createElement("div"); 201 this.preview.classList.add(css); 202 if (this.config.button) { 203 this.config.button.after(this.preview); 204 } 205 } 206 this.preview.innerHTML = `<div><img src="${url}"></div><div>${lnglat} <button class="ui mini button" type="button">Clear</button></div>`; 207 const button = this.preview.querySelector("button"); 208 if (button) { 209 button.addEventListener("click", this.clearselected); 210 } 211 } 212 } 213 214 /** 215 * Clear the selected value and preview 216 */ 217 clearselected () { 218 if (this.preview) { 219 this.preview.remove(); 220 this.preview = undefined; 221 } 222 this.eles.forEach((ele) => { 223 ele.value = ""; 224 }); 225 } 226 227 /** 228 * Remove the GeoPicker modal from the DOM 229 */ 230 removemodal () { 231 if (this.modal) { 232 this.modal.remove(); 233 this.modal = undefined; 234 } 235 } 236 237 /** 238 * Get the form location field(s) based on the passed input params 239 * 240 */ 241 fieldelement (eles) { 242 if (eles.constructor.name == "Object" && (eles.lng && eles.lat || eles.lnglat)) { 243 if (eles.lnglat) { 244 eles = [eles.lnglat]; 245 } else { 246 eles = [eles.lng, eles.lat]; 247 } 248 } else if (eles.constructor.name == "HTMLInputElement") { 249 eles = [eles]; 250 } else if (eles.constructor.name == "String") { 251 eles = [eles]; 252 } 253 if (eles.constructor.name == "Array") {
254 eles.forEach((item, i) => { 255 if (item.constructor.name == "String") { 256 eles[i] = document.getElementById(item); 257 } 258 }); 259 eles.forEach((item) => { 260 if (item.constructor.name != "HTMLInputElement") { 261 throw new Error("Can't find form fields."); 262 } 263 }); 264 return eles; 265 } 266 throw new Error("Can't find form fields."); 267 } 268}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.