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>