github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cmd/geoviz/templates/index.tmpl.html (about) 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>CockroachDB GeoVIZ</title> 5 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> 6 <meta name="viewport" content="initial-scale=1.0"> 7 <meta charset="utf-8"> 8 <style> 9 /* Always set the map height explicitly to define the size of the div 10 * element that contains the map. */ 11 #map { 12 height: 100%; 13 } 14 #geoviz-input { 15 position: absolute; 16 bottom: 10px; 17 left: 10px; 18 z-index: 5; 19 background-color: #fff; 20 padding: 5px; 21 border: 1px solid #999; 22 text-align: center; 23 font-family: 'Roboto','sans-serif'; 24 line-height: 30px; 25 padding-left: 10px; 26 } 27 #legend { 28 position: absolute; 29 top: 80px; 30 right: 10px; 31 max-height: 60%; 32 z-index: 5; 33 background-color: #fff; 34 padding: 5px; 35 border: 1px solid #999; 36 font-family: 'Roboto','sans-serif'; 37 overflow: auto; 38 } 39 html, body { 40 height: 100%; 41 margin: 0; 42 padding: 0; 43 } 44 </style> 45 </head> 46 <body> 47 <div id="legend"> 48 <h3>Legend</h3> 49 <div id="legend-items"> 50 </div> 51 <div> 52 <hr/> 53 <label> 54 <input type="checkbox" id="show-labels" name="show-labels" checked> 55 Show Labels 56 </label> 57 </div> 58 </div> 59 <div id="geoviz-input"> 60 <form id="form-geoviz-input" method="POST" action="/load"> 61 <div> 62 <p> 63 Each line should contain four fields in CSV Format: <code>op,name,color,ewkt/cellid</code>. Name/Color can be blank to be randomized.<br/> 64 Valid ops are: "drawgeography", "innercovering", "drawcellid", "drawcelltoken", "covering", "interiorcovering". 65 </p> 66 <textarea name="data" rows="10" cols="160"> 67 drawcelltoken,An International Cell Token,,869 68 drawgeography,California,,"POLYGON((-124.4009 41.9983,-123.6237 42.0024,-123.1526 42.0126,-122.0073 42.0075,-121.2369 41.9962,-119.9982 41.9983,-120.0037 39.0021,-117.9575 37.5555,-116.3699 36.3594,-114.6368 35.0075,-114.6382 34.9659,-114.6286 34.9107,-114.6382 34.8758,-114.5970 34.8454,-114.5682 34.7890,-114.4968 34.7269,-114.4501 34.6648,-114.4597 34.6581,-114.4322 34.5869,-114.3787 34.5235,-114.3869 34.4601,-114.3361 34.4500,-114.3031 34.4375,-114.2674 34.4024,-114.1864 34.3559,-114.1383 34.3049,-114.1315 34.2561,-114.1651 34.2595,-114.2249 34.2044,-114.2221 34.1914,-114.2908 34.1720,-114.3237 34.1368,-114.3622 34.1186,-114.4089 34.1118,-114.4363 34.0856,-114.4336 34.0276,-114.4652 34.0117,-114.5119 33.9582,-114.5366 33.9308,-114.5091 33.9058,-114.5256 33.8613,-114.5215 33.8248,-114.5050 33.7597,-114.4940 33.7083,-114.5284 33.6832,-114.5242 33.6363,-114.5393 33.5895,-114.5242 33.5528,-114.5586 33.5311,-114.5778 33.5070,-114.6245 33.4418,-114.6506 33.4142,-114.7055 33.4039,-114.6973 33.3546,-114.7302 33.3041,-114.7206 33.2858,-114.6808 33.2754,-114.6698 33.2582,-114.6904 33.2467,-114.6794 33.1720,-114.7083 33.0904,-114.6918 33.0858,-114.6629 33.0328,-114.6451 33.0501,-114.6286 33.0305,-114.5888 33.0282,-114.5750 33.0351,-114.5174 33.0328,-114.4913 32.9718,-114.4775 32.9764,-114.4844 32.9372,-114.4679 32.8427,-114.5091 32.8161,-114.5311 32.7850,-114.5284 32.7573,-114.5641 32.7503,-114.6162 32.7353,-114.6986 32.7480,-114.7220 32.7191,-115.1944 32.6868,-117.3395 32.5121,-117.4823 32.7838,-117.5977 33.0501,-117.6814 33.2341,-118.0591 33.4578,-118.6290 33.5403,-118.7073 33.7928,-119.3706 33.9582,-120.0050 34.1925,-120.7164 34.2561,-120.9128 34.5360,-120.8427 34.9749,-121.1325 35.2131,-121.3220 35.5255,-121.8013 35.9691,-122.1446 36.2808,-122.1721 36.7268,-122.6871 37.2227,-122.8903 37.7783,-123.2378 37.8965,-123.3202 38.3449,-123.8338 38.7423,-123.9793 38.9946,-124.0329 39.3088,-124.0823 39.7642,-124.5314 40.1663,-124.6509 40.4658,-124.3144 41.0110,-124.3419 41.2386,-124.4545 41.7170,-124.4009 41.9983,-124.4009 41.9983))" 69 drawgeography,San Francisco,,"POINT(-122.4194 37.7749)" 70 drawgeography,"San Francisco to LA via Bakersfield",orange,"LINESTRING(-122.4194 37.7749, -119.0187 35.3733,-118.2437 34.0522)" 71 covering,New Mexico Covering,purple,"POLYGON((-109.0448 36.9971,-109.0489 31.3337,-108.2140 31.3349,-108.2071 31.7795,-106.5317 31.7830,-106.6223 32.0034,-103.0696 31.9999,-103.0023 36.9982,-109.0475 36.9982,-109.0448 36.9971))" 72 </textarea> 73 </div> 74 <div> 75 <input type="submit" value="GeoVIZ!"> 76 </div> 77 </form> 78 </div> 79 <div id="map"> 80 </div> 81 <script> 82 var map; 83 var labels = []; 84 var allObjItems = []; 85 86 // initMap will initialize a Google Map, and submit the form defaults to the server 87 // for re-display. 88 function initMap() { 89 map = new google.maps.Map(document.getElementById('map'), { 90 center: {lat: -34.397, lng: 150.644}, 91 zoom: 2 92 }); 93 $("#form-geoviz-input").submit(); 94 } 95 96 // When the show labels button is clicked (or unclicked), show (or hide) all labels on the map. 97 $("#show-labels").click(function() { 98 const checked = $(this).is(':checked'); 99 var idx = 0; 100 for (const objItems of allObjItems) { 101 var itemChecked = $('input[name="show-object[]"][value="' + idx + '"]').is(':checked'); 102 for (const label of objItems.labels) { 103 label.setMap((itemChecked && checked) ? map : null); 104 } 105 idx++; 106 } 107 }); 108 109 // setMapOnObjs will set visibility on each object depending on whether the show labels 110 // and legend is clicked. 111 function setMapOnObjs(objItems, visible) { 112 for (const obj of objItems.objs) { 113 obj.setMap(visible ? map : null); 114 } 115 for (const infoWindow of objItems.infowindows) { 116 if (!visible) { 117 infoWindow.close(); 118 } 119 } 120 const labelChecked = $('#show-labels').is(':checked'); 121 for (const label of objItems.labels) { 122 label.setMap((labelChecked && visible) ? map : null); 123 } 124 } 125 126 $("#form-geoviz-input").submit(function(event) { 127 event.preventDefault(); 128 129 const form = $(this); 130 const url = form.attr('action'); 131 132 // Remove everything from the map. 133 for (const objItems of allObjItems) { 134 setMapOnObjs(objItems, false); 135 } 136 137 // Reset everything again. 138 allObjItems = []; 139 140 // addMarker adds a marker on the map, modifying objItems with 141 // new marker metadata. 142 // Returns an object with the marker and infowindow. 143 function addMarker(marker, objItems, visible) { 144 const infowindow = new google.maps.InfoWindow({ 145 content: marker.content, 146 }); 147 const p = new google.maps.Marker({ 148 ...marker, 149 map: map, 150 visible: visible, 151 }); 152 objItems.objs.push(p); 153 objItems.infowindows.push(infowindow); 154 p.addListener('click', function() { 155 infowindow.open(map, p); 156 }); 157 return {marker: p, infowindow}; 158 } 159 160 $.ajax({ 161 type: "POST", 162 url: url, 163 data: form.serialize(), 164 success: function(data) { 165 const bounds = new google.maps.LatLngBounds(); 166 var list = $("<div/>"); 167 for (const obj of data.objects) { 168 list.append( 169 '<label style="color:' + obj.color + '">' + 170 '<input type="checkbox" name="show-object[]" value="' + allObjItems.length + '" checked> ' + 171 obj.title + '</label><br/>' 172 ); 173 174 const objItems = { 175 objs: [], 176 infowindows: [], 177 labels: [], 178 }; 179 if (obj.polylines) { 180 for (const polyline of obj.polylines) { 181 const p = new google.maps.Polyline({ 182 ...polyline, 183 map: map, 184 }); 185 objItems.objs.push(p); 186 var initial = true; 187 for (const loc of polyline.path) { 188 bounds.extend(loc); 189 const params = { 190 ...polyline.marker, 191 position: loc, 192 icon: { 193 url: "http://maps.google.com/mapfiles/kml/pal4/icon57.png", 194 origin: new google.maps.Point(0, 0), 195 scaledSize: new google.maps.Size(24, 24), 196 anchor: new google.maps.Point(12, 12), 197 }, 198 }; 199 const marker = addMarker(params, objItems, true); 200 if (initial) { 201 p.addListener('click', function() { 202 marker.infowindow.open(map, marker.marker); 203 }); 204 initial = false; 205 } 206 } 207 } 208 } 209 if (obj.polygons) { 210 for (const polygon of obj.polygons) { 211 const p = new google.maps.Polygon({ 212 ...polygon, 213 map: map, 214 }); 215 objItems.objs.push(p); 216 var polygonBound = new google.maps.LatLngBounds(); 217 for (const loc of polygon.paths[0]) { 218 polygonBound.extend(loc); 219 bounds.extend(loc); 220 } 221 polygon.marker.position = polygonBound.getCenter(); 222 const marker = addMarker(polygon.marker, objItems, false); 223 p.addListener('click', function() { 224 marker.infowindow.open(map, marker.marker); 225 }); 226 const label = new MapLabel({ 227 text: polygon.label, 228 position: polygonBound.getCenter(), 229 fontSize: 16, 230 align: 'center' 231 }); 232 if ($('#show-labels').is(':checked')) { 233 label.setMap(map); 234 } 235 objItems.labels.push(label); 236 } 237 } 238 if (obj.markers) { 239 for (const marker of obj.markers) { 240 addMarker(marker, objItems, true); 241 bounds.extend(marker.position); 242 } 243 } 244 allObjItems.push(objItems); 245 } 246 map.fitBounds(bounds); 247 $("#legend-items").html(list); 248 249 $('input[name="show-object[]"]').click(function() { 250 const checked = $(this).is(':checked'); 251 const idx = $(this).attr('value'); 252 setMapOnObjs(allObjItems[idx], checked); 253 }); 254 }, 255 error: function(xhr) { 256 alert(xhr.responseText); 257 } 258 }); 259 }); 260 </script> 261 {{if .APIKey}} 262 <script src="https://maps.googleapis.com/maps/api/js?key={{.APIKey}}&callback=initMap"></script> 263 {{else}} 264 <script src="https://maps.googleapis.com/maps/api/js?callback=initMap"></script> 265 {{end}} 266 <script> 267 /** 268 * @license 269 * 270 * Copyright 2011 Google Inc. 271 * 272 * Licensed under the Apache License, Version 2.0 (the "License"); 273 * you may not use this file except in compliance with the License. 274 * You may obtain a copy of the License at 275 * 276 * http://www.apache.org/licenses/LICENSE-2.0 277 * 278 * Unless required by applicable law or agreed to in writing, software 279 * distributed under the License is distributed on an "AS IS" BASIS, 280 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 281 * See the License for the specific language governing permissions and 282 * limitations under the License. 283 */ 284 285 /** 286 * @fileoverview Map Label. 287 * 288 * @author Luke Mahe (lukem@google.com), 289 * Chris Broadfoot (cbro@google.com) 290 */ 291 292 /** 293 * Creates a new Map Label 294 * @constructor 295 * @extends google.maps.OverlayView 296 * @param {Object.<string, *>=} opt_options Optional properties to set. 297 */ 298 function MapLabel(opt_options) { 299 this.set('fontFamily', 'sans-serif'); 300 this.set('fontSize', 12); 301 this.set('fontColor', '#000000'); 302 this.set('strokeWeight', 4); 303 this.set('strokeColor', '#ffffff'); 304 this.set('align', 'center'); 305 306 this.set('zIndex', 1e3); 307 308 this.setValues(opt_options); 309 } 310 311 MapLabel.prototype = new google.maps.OverlayView; 312 313 window['MapLabel'] = MapLabel; 314 315 /** @inheritDoc */ 316 MapLabel.prototype.changed = function(prop) { 317 switch (prop) { 318 case 'fontFamily': 319 case 'fontSize': 320 case 'fontColor': 321 case 'strokeWeight': 322 case 'strokeColor': 323 case 'align': 324 case 'text': 325 return this.drawCanvas_(); 326 case 'maxZoom': 327 case 'minZoom': 328 case 'position': 329 return this.draw(); 330 } 331 }; 332 333 /** 334 * Draws the label to the canvas 2d context. 335 * @private 336 */ 337 MapLabel.prototype.drawCanvas_ = function() { 338 var canvas = this.canvas_; 339 if (!canvas) return; 340 341 var style = canvas.style; 342 style.zIndex = /** @type number */(this.get('zIndex')); 343 344 var ctx = canvas.getContext('2d'); 345 ctx.clearRect(0, 0, canvas.width, canvas.height); 346 ctx.strokeStyle = this.get('strokeColor'); 347 ctx.fillStyle = this.get('fontColor'); 348 ctx.font = this.get('fontSize') + 'px ' + this.get('fontFamily'); 349 350 var strokeWeight = Number(this.get('strokeWeight')); 351 352 var text = this.get('text'); 353 if (text) { 354 if (strokeWeight) { 355 ctx.lineWidth = strokeWeight; 356 ctx.strokeText(text, strokeWeight, strokeWeight); 357 } 358 359 ctx.fillText(text, strokeWeight, strokeWeight); 360 361 var textMeasure = ctx.measureText(text); 362 var textWidth = textMeasure.width + strokeWeight; 363 style.marginLeft = this.getMarginLeft_(textWidth) + 'px'; 364 // Bring actual text top in line with desired latitude. 365 // Cheaper than calculating height of text. 366 style.marginTop = '-0.4em'; 367 } 368 }; 369 370 /** 371 * @inheritDoc 372 */ 373 MapLabel.prototype.onAdd = function() { 374 var canvas = this.canvas_ = document.createElement('canvas'); 375 var style = canvas.style; 376 style.position = 'absolute'; 377 378 var ctx = canvas.getContext('2d'); 379 ctx.lineJoin = 'round'; 380 ctx.textBaseline = 'top'; 381 382 this.drawCanvas_(); 383 384 var panes = this.getPanes(); 385 if (panes) { 386 panes.mapPane.appendChild(canvas); 387 } 388 }; 389 MapLabel.prototype['onAdd'] = MapLabel.prototype.onAdd; 390 391 /** 392 * Gets the appropriate margin-left for the canvas. 393 * @private 394 * @param {number} textWidth the width of the text, in pixels. 395 * @return {number} the margin-left, in pixels. 396 */ 397 MapLabel.prototype.getMarginLeft_ = function(textWidth) { 398 switch (this.get('align')) { 399 case 'left': 400 return 0; 401 case 'right': 402 return -textWidth; 403 } 404 return textWidth / -2; 405 }; 406 407 /** 408 * @inheritDoc 409 */ 410 MapLabel.prototype.draw = function() { 411 var projection = this.getProjection(); 412 413 if (!projection) { 414 // The map projection is not ready yet so do nothing 415 return; 416 } 417 418 if (!this.canvas_) { 419 // onAdd has not been called yet. 420 return; 421 } 422 423 var latLng = /** @type {google.maps.LatLng} */ (this.get('position')); 424 if (!latLng) { 425 return; 426 } 427 var pos = projection.fromLatLngToDivPixel(latLng); 428 429 var style = this.canvas_.style; 430 431 style['top'] = pos.y + 'px'; 432 style['left'] = pos.x + 'px'; 433 434 style['visibility'] = this.getVisible_(); 435 }; 436 MapLabel.prototype['draw'] = MapLabel.prototype.draw; 437 438 /** 439 * Get the visibility of the label. 440 * @private 441 * @return {string} blank string if visible, 'hidden' if invisible. 442 */ 443 MapLabel.prototype.getVisible_ = function() { 444 var minZoom = /** @type number */(this.get('minZoom')); 445 var maxZoom = /** @type number */(this.get('maxZoom')); 446 447 if (minZoom === undefined && maxZoom === undefined) { 448 return ''; 449 } 450 451 var map = this.getMap(); 452 if (!map) { 453 return ''; 454 } 455 456 var mapZoom = map.getZoom(); 457 if (mapZoom < minZoom || mapZoom > maxZoom) { 458 return 'hidden'; 459 } 460 return ''; 461 }; 462 463 /** 464 * @inheritDoc 465 */ 466 MapLabel.prototype.onRemove = function() { 467 var canvas = this.canvas_; 468 if (canvas && canvas.parentNode) { 469 canvas.parentNode.removeChild(canvas); 470 } 471 }; 472 MapLabel.prototype['onRemove'] = MapLabel.prototype.onRemove; 473 </script> 474 </body> 475 </html>