Compare View

switch
from
...
to
 
Commits (2)

Diff

Showing 7 changed files Side-by-side Diff

public/scripts/grade.js View file @ 77ade48
... ... @@ -0,0 +1,11 @@
  1 +var unirest = require('unirest');
  2 +
  3 +function getGrade(long, lat){
  4 + unirest.get("https://crimescore.p.mashape.com/crimescore?f=json&id=174&lat=37.782551&lon=-122.445368")
  5 + .header("X-Mashape-Key", "gj4g6D3zxKmshys7yyf9KNbuywOxp1ueazFjsn6n2fK4sccIGA")
  6 + .header("Accept", "application/json")
  7 + .end(function (result) {
  8 + res.render('pages/tempcrime', result);
  9 + console.log(result.status, result.headers, result.body);
  10 + });
  11 +}
0 12 \ No newline at end of file
public/scripts/routeboxer.js View file @ 77ade48
... ... @@ -0,0 +1,582 @@
  1 +/**
  2 + * @name RouteBoxer
  3 + * @version 1.0
  4 + * @copyright (c) 2010 Google Inc.
  5 + * @author Thor Mitchell
  6 + *
  7 + * @fileoverview The RouteBoxer class takes a path, such as the Polyline for a
  8 + * route generated by a Directions request, and generates a set of LatLngBounds
  9 + * objects that are guaranteed to contain every point within a given distance
  10 + * of that route. These LatLngBounds objects can then be used to generate
  11 + * requests to spatial search services that support bounds filtering (such as
  12 + * the Google Maps Data API) in order to implement search along a route.
  13 + * <br/><br/>
  14 + * RouteBoxer overlays a grid of the specified size on the route, identifies
  15 + * every grid cell that the route passes through, and generates a set of bounds
  16 + * that cover all of these cells, and their nearest neighbours. Consequently
  17 + * the bounds returned will extend up to ~3x the specified distance from the
  18 + * route in places.
  19 + */
  20 +
  21 +/*
  22 + * Licensed under the Apache License, Version 2.0 (the "License");
  23 + * you may not use this file except in compliance with the License.
  24 + * You may obtain a copy of the License at
  25 + *
  26 + * http://www.apache.org/licenses/LICENSE-2.0
  27 + *
  28 + * Unless required by applicable law or agreed to in writing, software
  29 + * distributed under the License is distributed on an "AS IS" BASIS,
  30 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  31 + * See the License for the specific language governing permissions and
  32 + * limitations under the License.
  33 + */
  34 +
  35 +/**
  36 + * Creates a new RouteBoxer
  37 + *
  38 + * @constructor
  39 + */
  40 +function RouteBoxer() {
  41 + this.R = 6371; // earth's mean radius in km
  42 + this.initGoogle();
  43 +}
  44 +
  45 +/**
  46 + * Generates boxes for a given route and distance
  47 + *
  48 + * @param {google.maps.LatLng[] | google.maps.Polyline} path The path along
  49 + * which to create boxes. The path object can be either an Array of
  50 + * google.maps.LatLng objects or a Maps API v2 or Maps API v3
  51 + * google.maps.Polyline object.
  52 + * @param {Number} range The distance in kms around the route that the generated
  53 + * boxes must cover.
  54 + * @return {google.maps.LatLngBounds[]} An array of boxes that covers the whole
  55 + * path.
  56 + */
  57 +RouteBoxer.prototype.box = function (path, range) {
  58 + // Two dimensional array representing the cells in the grid overlaid on the path
  59 + this.grid_ = null;
  60 +
  61 + // Array that holds the latitude coordinate of each vertical grid line
  62 + this.latGrid_ = [];
  63 +
  64 + // Array that holds the longitude coordinate of each horizontal grid line
  65 + this.lngGrid_ = [];
  66 +
  67 + // Array of bounds that cover the whole route formed by merging cells that
  68 + // the route intersects first horizontally, and then vertically
  69 + this.boxesX_ = [];
  70 +
  71 + // Array of bounds that cover the whole route formed by merging cells that
  72 + // the route intersects first vertically, and then horizontally
  73 + this.boxesY_ = [];
  74 +
  75 + // The array of LatLngs representing the vertices of the path
  76 + var vertices = null;
  77 +
  78 + // If necessary convert the path into an array of LatLng objects
  79 + if (path instanceof Array) {
  80 + // already an arry of LatLngs (eg. v3 overview_path)
  81 + vertices = path;
  82 + } else if (path instanceof google.maps.Polyline) {
  83 + if (path.getPath) {
  84 + // v3 Maps API Polyline object
  85 + vertices = new Array(path.getPath().getLength());
  86 + for (var i = 0; i < vertices.length; i++) {
  87 + vertices[i] = path.getPath().getAt(i);
  88 + }
  89 + } else {
  90 + // v2 Maps API Polyline object
  91 + vertices = new Array(path.getVertexCount());
  92 + for (var j = 0; j < vertices.length; j++) {
  93 + vertices[j] = path.getVertex(j);
  94 + }
  95 + }
  96 + }
  97 +
  98 + // Build the grid that is overlaid on the route
  99 + this.buildGrid_(vertices, range);
  100 +
  101 + // Identify the grid cells that the route intersects
  102 + this.findIntersectingCells_(vertices);
  103 +
  104 + // Merge adjacent intersected grid cells (and their neighbours) into two sets
  105 + // of bounds, both of which cover them completely
  106 + this.mergeIntersectingCells_();
  107 +
  108 + // Return the set of merged bounds that has the fewest elements
  109 + return (this.boxesX_.length <= this.boxesY_.length ?
  110 + this.boxesX_ :
  111 + this.boxesY_);
  112 +};
  113 +
  114 +/**
  115 + * Generates boxes for a given route and distance
  116 + *
  117 + * @param {LatLng[]} vertices The vertices of the path over which to lay the grid
  118 + * @param {Number} range The spacing of the grid cells.
  119 + */
  120 +RouteBoxer.prototype.buildGrid_ = function (vertices, range) {
  121 +
  122 + // Create a LatLngBounds object that contains the whole path
  123 + var routeBounds = new google.maps.LatLngBounds();
  124 + for (var i = 0; i < vertices.length; i++) {
  125 + routeBounds.extend(vertices[i]);
  126 + }
  127 +
  128 + // Find the center of the bounding box of the path
  129 + var routeBoundsCenter = routeBounds.getCenter();
  130 +
  131 + // Starting from the center define grid lines outwards vertically until they
  132 + // extend beyond the edge of the bounding box by more than one cell
  133 + this.latGrid_.push(routeBoundsCenter.lat());
  134 +
  135 + // Add lines from the center out to the north
  136 + this.latGrid_.push(routeBoundsCenter.rhumbDestinationPoint(0, range).lat());
  137 + for (i = 2; this.latGrid_[i - 2] < routeBounds.getNorthEast().lat(); i++) {
  138 + this.latGrid_.push(routeBoundsCenter.rhumbDestinationPoint(0, range * i).lat());
  139 + }
  140 +
  141 + // Add lines from the center out to the south
  142 + for (i = 1; this.latGrid_[1] > routeBounds.getSouthWest().lat(); i++) {
  143 + this.latGrid_.unshift(routeBoundsCenter.rhumbDestinationPoint(180, range * i).lat());
  144 + }
  145 +
  146 + // Starting from the center define grid lines outwards horizontally until they
  147 + // extend beyond the edge of the bounding box by more than one cell
  148 + this.lngGrid_.push(routeBoundsCenter.lng());
  149 +
  150 + // Add lines from the center out to the east
  151 + this.lngGrid_.push(routeBoundsCenter.rhumbDestinationPoint(90, range).lng());
  152 + for (i = 2; this.lngGrid_[i - 2] < routeBounds.getNorthEast().lng(); i++) {
  153 + this.lngGrid_.push(routeBoundsCenter.rhumbDestinationPoint(90, range * i).lng());
  154 + }
  155 +
  156 + // Add lines from the center out to the west
  157 + for (i = 1; this.lngGrid_[1] > routeBounds.getSouthWest().lng(); i++) {
  158 + this.lngGrid_.unshift(routeBoundsCenter.rhumbDestinationPoint(270, range * i).lng());
  159 + }
  160 +
  161 + // Create a two dimensional array representing this grid
  162 + this.grid_ = new Array(this.lngGrid_.length);
  163 + for (i = 0; i < this.grid_.length; i++) {
  164 + this.grid_[i] = new Array(this.latGrid_.length);
  165 + }
  166 +};
  167 +
  168 +/**
  169 + * Find all of the cells in the overlaid grid that the path intersects
  170 + *
  171 + * @param {LatLng[]} vertices The vertices of the path
  172 + */
  173 +RouteBoxer.prototype.findIntersectingCells_ = function (vertices) {
  174 + // Find the cell where the path begins
  175 + var hintXY = this.getCellCoords_(vertices[0]);
  176 +
  177 + // Mark that cell and it's neighbours for inclusion in the boxes
  178 + this.markCell_(hintXY);
  179 +
  180 + // Work through each vertex on the path identifying which grid cell it is in
  181 + for (var i = 1; i < vertices.length; i++) {
  182 + // Use the known cell of the previous vertex to help find the cell of this vertex
  183 + var gridXY = this.getGridCoordsFromHint_(vertices[i], vertices[i - 1], hintXY);
  184 +
  185 + if (gridXY[0] === hintXY[0] && gridXY[1] === hintXY[1]) {
  186 + // This vertex is in the same cell as the previous vertex
  187 + // The cell will already have been marked for inclusion in the boxes
  188 + continue;
  189 +
  190 + } else if ((Math.abs(hintXY[0] - gridXY[0]) === 1 && hintXY[1] === gridXY[1]) ||
  191 + (hintXY[0] === gridXY[0] && Math.abs(hintXY[1] - gridXY[1]) === 1)) {
  192 + // This vertex is in a cell that shares an edge with the previous cell
  193 + // Mark this cell and it's neighbours for inclusion in the boxes
  194 + this.markCell_(gridXY);
  195 +
  196 + } else {
  197 + // This vertex is in a cell that does not share an edge with the previous
  198 + // cell. This means that the path passes through other cells between
  199 + // this vertex and the previous vertex, and we must determine which cells
  200 + // it passes through
  201 + this.getGridIntersects_(vertices[i - 1], vertices[i], hintXY, gridXY);
  202 + }
  203 +
  204 + // Use this cell to find and compare with the next one
  205 + hintXY = gridXY;
  206 + }
  207 +};
  208 +
  209 +/**
  210 + * Find the cell a path vertex is in by brute force iteration over the grid
  211 + *
  212 + * @param {LatLng[]} latlng The latlng of the vertex
  213 + * @return {Number[][]} The cell coordinates of this vertex in the grid
  214 + */
  215 +RouteBoxer.prototype.getCellCoords_ = function (latlng) {
  216 + for (var x = 0; this.lngGrid_[x] < latlng.lng(); x++) {}
  217 + for (var y = 0; this.latGrid_[y] < latlng.lat(); y++) {}
  218 + return ([x - 1, y - 1]);
  219 +};
  220 +
  221 +/**
  222 + * Find the cell a path vertex is in based on the known location of a nearby
  223 + * vertex. This saves searching the whole grid when working through vertices
  224 + * on the polyline that are likely to be in close proximity to each other.
  225 + *
  226 + * @param {LatLng[]} latlng The latlng of the vertex to locate in the grid
  227 + * @param {LatLng[]} hintlatlng The latlng of the vertex with a known location
  228 + * @param {Number[]} hint The cell containing the vertex with a known location
  229 + * @return {Number[]} The cell coordinates of the vertex to locate in the grid
  230 + */
  231 +RouteBoxer.prototype.getGridCoordsFromHint_ = function (latlng, hintlatlng, hint) {
  232 + var x, y;
  233 + if (latlng.lng() > hintlatlng.lng()) {
  234 + for (x = hint[0]; this.lngGrid_[x + 1] < latlng.lng(); x++) {}
  235 + } else {
  236 + for (x = hint[0]; this.lngGrid_[x] > latlng.lng(); x--) {}
  237 + }
  238 +
  239 + if (latlng.lat() > hintlatlng.lat()) {
  240 + for (y = hint[1]; this.latGrid_[y + 1] < latlng.lat(); y++) {}
  241 + } else {
  242 + for (y = hint[1]; this.latGrid_[y] > latlng.lat(); y--) {}
  243 + }
  244 +
  245 + return ([x, y]);
  246 +};
  247 +
  248 +
  249 +/**
  250 + * Identify the grid squares that a path segment between two vertices
  251 + * intersects with by:
  252 + * 1. Finding the bearing between the start and end of the segment
  253 + * 2. Using the delta between the lat of the start and the lat of each
  254 + * latGrid boundary to find the distance to each latGrid boundary
  255 + * 3. Finding the lng of the intersection of the line with each latGrid
  256 + * boundary using the distance to the intersection and bearing of the line
  257 + * 4. Determining the x-coord on the grid of the point of intersection
  258 + * 5. Filling in all squares between the x-coord of the previous intersection
  259 + * (or start) and the current one (or end) at the current y coordinate,
  260 + * which is known for the grid line being intersected
  261 + *
  262 + * @param {LatLng} start The latlng of the vertex at the start of the segment
  263 + * @param {LatLng} end The latlng of the vertex at the end of the segment
  264 + * @param {Number[]} startXY The cell containing the start vertex
  265 + * @param {Number[]} endXY The cell containing the vend vertex
  266 + */
  267 +RouteBoxer.prototype.getGridIntersects_ = function (start, end, startXY, endXY) {
  268 + var edgePoint, edgeXY, i;
  269 + var brng = start.rhumbBearingTo(end); // Step 1.
  270 +
  271 + var hint = start;
  272 + var hintXY = startXY;
  273 +
  274 + // Handle a line segment that travels south first
  275 + if (end.lat() > start.lat()) {
  276 + // Iterate over the east to west grid lines between the start and end cells
  277 + for (i = startXY[1] + 1; i <= endXY[1]; i++) {
  278 + // Find the latlng of the point where the path segment intersects with
  279 + // this grid line (Step 2 & 3)
  280 + edgePoint = this.getGridIntersect_(start, brng, this.latGrid_[i]);
  281 +
  282 + // Find the cell containing this intersect point (Step 4)
  283 + edgeXY = this.getGridCoordsFromHint_(edgePoint, hint, hintXY);
  284 +
  285 + // Mark every cell the path has crossed between this grid and the start,
  286 + // or the previous east to west grid line it crossed (Step 5)
  287 + this.fillInGridSquares_(hintXY[0], edgeXY[0], i - 1);
  288 +
  289 + // Use the point where it crossed this grid line as the reference for the
  290 + // next iteration
  291 + hint = edgePoint;
  292 + hintXY = edgeXY;
  293 + }
  294 +
  295 + // Mark every cell the path has crossed between the last east to west grid
  296 + // line it crossed and the end (Step 5)
  297 + this.fillInGridSquares_(hintXY[0], endXY[0], i - 1);
  298 +
  299 + } else {
  300 + // Iterate over the east to west grid lines between the start and end cells
  301 + for (i = startXY[1]; i > endXY[1]; i--) {
  302 + // Find the latlng of the point where the path segment intersects with
  303 + // this grid line (Step 2 & 3)
  304 + edgePoint = this.getGridIntersect_(start, brng, this.latGrid_[i]);
  305 +
  306 + // Find the cell containing this intersect point (Step 4)
  307 + edgeXY = this.getGridCoordsFromHint_(edgePoint, hint, hintXY);
  308 +
  309 + // Mark every cell the path has crossed between this grid and the start,
  310 + // or the previous east to west grid line it crossed (Step 5)
  311 + this.fillInGridSquares_(hintXY[0], edgeXY[0], i);
  312 +
  313 + // Use the point where it crossed this grid line as the reference for the
  314 + // next iteration
  315 + hint = edgePoint;
  316 + hintXY = edgeXY;
  317 + }
  318 +
  319 + // Mark every cell the path has crossed between the last east to west grid
  320 + // line it crossed and the end (Step 5)
  321 + this.fillInGridSquares_(hintXY[0], endXY[0], i);
  322 +
  323 + }
  324 +};
  325 +
  326 +/**
  327 + * Find the latlng at which a path segment intersects with a given
  328 + * line of latitude
  329 + *
  330 + * @param {LatLng} start The vertex at the start of the path segment
  331 + * @param {Number} brng The bearing of the line from start to end
  332 + * @param {Number} gridLineLat The latitude of the grid line being intersected
  333 + * @return {LatLng} The latlng of the point where the path segment intersects
  334 + * the grid line
  335 + */
  336 +RouteBoxer.prototype.getGridIntersect_ = function (start, brng, gridLineLat) {
  337 + var d = this.R * ((gridLineLat.toRad() - start.lat().toRad()) / Math.cos(brng.toRad()));
  338 + return start.rhumbDestinationPoint(brng, d);
  339 +};
  340 +
  341 +/**
  342 + * Mark all cells in a given row of the grid that lie between two columns
  343 + * for inclusion in the boxes
  344 + *
  345 + * @param {Number} startx The first column to include
  346 + * @param {Number} endx The last column to include
  347 + * @param {Number} y The row of the cells to include
  348 + */
  349 +RouteBoxer.prototype.fillInGridSquares_ = function (startx, endx, y) {
  350 + var x;
  351 + if (startx < endx) {
  352 + for (x = startx; x <= endx; x++) {
  353 + this.markCell_([x, y]);
  354 + }
  355 + } else {
  356 + for (x = startx; x >= endx; x--) {
  357 + this.markCell_([x, y]);
  358 + }
  359 + }
  360 +};
  361 +
  362 +/**
  363 + * Mark a cell and the 8 immediate neighbours for inclusion in the boxes
  364 + *
  365 + * @param {Number[]} square The cell to mark
  366 + */
  367 +RouteBoxer.prototype.markCell_ = function (cell) {
  368 + var x = cell[0];
  369 + var y = cell[1];
  370 + this.grid_[x - 1][y - 1] = 1;
  371 + this.grid_[x][y - 1] = 1;
  372 + this.grid_[x + 1][y - 1] = 1;
  373 + this.grid_[x - 1][y] = 1;
  374 + this.grid_[x][y] = 1;
  375 + this.grid_[x + 1][y] = 1;
  376 + this.grid_[x - 1][y + 1] = 1;
  377 + this.grid_[x][y + 1] = 1;
  378 + this.grid_[x + 1][y + 1] = 1;
  379 +};
  380 +
  381 +/**
  382 + * Create two sets of bounding boxes, both of which cover all of the cells that
  383 + * have been marked for inclusion.
  384 + *
  385 + * The first set is created by combining adjacent cells in the same column into
  386 + * a set of vertical rectangular boxes, and then combining boxes of the same
  387 + * height that are adjacent horizontally.
  388 + *
  389 + * The second set is created by combining adjacent cells in the same row into
  390 + * a set of horizontal rectangular boxes, and then combining boxes of the same
  391 + * width that are adjacent vertically.
  392 + *
  393 + */
  394 +RouteBoxer.prototype.mergeIntersectingCells_ = function () {
  395 + var x, y, box;
  396 +
  397 + // The box we are currently expanding with new cells
  398 + var currentBox = null;
  399 +
  400 + // Traverse the grid a row at a time
  401 + for (y = 0; y < this.grid_[0].length; y++) {
  402 + for (x = 0; x < this.grid_.length; x++) {
  403 +
  404 + if (this.grid_[x][y]) {
  405 + // This cell is marked for inclusion. If the previous cell in this
  406 + // row was also marked for inclusion, merge this cell into it's box.
  407 + // Otherwise start a new box.
  408 + box = this.getCellBounds_([x, y]);
  409 + if (currentBox) {
  410 + currentBox.extend(box.getNorthEast());
  411 + } else {
  412 + currentBox = box;
  413 + }
  414 +
  415 + } else {
  416 + // This cell is not marked for inclusion. If the previous cell was
  417 + // marked for inclusion, merge it's box with a box that spans the same
  418 + // columns from the row below if possible.
  419 + this.mergeBoxesY_(currentBox);
  420 + currentBox = null;
  421 + }
  422 + }
  423 + // If the last cell was marked for inclusion, merge it's box with a matching
  424 + // box from the row below if possible.
  425 + this.mergeBoxesY_(currentBox);
  426 + currentBox = null;
  427 + }
  428 +
  429 + // Traverse the grid a column at a time
  430 + for (x = 0; x < this.grid_.length; x++) {
  431 + for (y = 0; y < this.grid_[0].length; y++) {
  432 + if (this.grid_[x][y]) {
  433 +
  434 + // This cell is marked for inclusion. If the previous cell in this
  435 + // column was also marked for inclusion, merge this cell into it's box.
  436 + // Otherwise start a new box.
  437 + if (currentBox) {
  438 + box = this.getCellBounds_([x, y]);
  439 + currentBox.extend(box.getNorthEast());
  440 + } else {
  441 + currentBox = this.getCellBounds_([x, y]);
  442 + }
  443 +
  444 + } else {
  445 + // This cell is not marked for inclusion. If the previous cell was
  446 + // marked for inclusion, merge it's box with a box that spans the same
  447 + // rows from the column to the left if possible.
  448 + this.mergeBoxesX_(currentBox);
  449 + currentBox = null;
  450 +
  451 + }
  452 + }
  453 + // If the last cell was marked for inclusion, merge it's box with a matching
  454 + // box from the column to the left if possible.
  455 + this.mergeBoxesX_(currentBox);
  456 + currentBox = null;
  457 + }
  458 +};
  459 +
  460 +/**
  461 + * Search for an existing box in an adjacent row to the given box that spans the
  462 + * same set of columns and if one is found merge the given box into it. If one
  463 + * is not found, append this box to the list of existing boxes.
  464 + *
  465 + * @param {LatLngBounds} The box to merge
  466 + */
  467 +RouteBoxer.prototype.mergeBoxesX_ = function (box) {
  468 + if (box !== null) {
  469 + for (var i = 0; i < this.boxesX_.length; i++) {
  470 + if (this.boxesX_[i].getNorthEast().lng() === box.getSouthWest().lng() &&
  471 + this.boxesX_[i].getSouthWest().lat() === box.getSouthWest().lat() &&
  472 + this.boxesX_[i].getNorthEast().lat() === box.getNorthEast().lat()) {
  473 + this.boxesX_[i].extend(box.getNorthEast());
  474 + return;
  475 + }
  476 + }
  477 + this.boxesX_.push(box);
  478 + }
  479 +};
  480 +
  481 +/**
  482 + * Search for an existing box in an adjacent column to the given box that spans
  483 + * the same set of rows and if one is found merge the given box into it. If one
  484 + * is not found, append this box to the list of existing boxes.
  485 + *
  486 + * @param {LatLngBounds} The box to merge
  487 + */
  488 +RouteBoxer.prototype.mergeBoxesY_ = function (box) {
  489 + if (box !== null) {
  490 + for (var i = 0; i < this.boxesY_.length; i++) {
  491 + if (this.boxesY_[i].getNorthEast().lat() === box.getSouthWest().lat() &&
  492 + this.boxesY_[i].getSouthWest().lng() === box.getSouthWest().lng() &&
  493 + this.boxesY_[i].getNorthEast().lng() === box.getNorthEast().lng()) {
  494 + this.boxesY_[i].extend(box.getNorthEast());
  495 + return;
  496 + }
  497 + }
  498 + this.boxesY_.push(box);
  499 + }
  500 +};
  501 +
  502 +/**
  503 + * Obtain the LatLng of the origin of a cell on the grid
  504 + *
  505 + * @param {Number[]} cell The cell to lookup.
  506 + * @return {LatLng} The latlng of the origin of the cell.
  507 + */
  508 +RouteBoxer.prototype.getCellBounds_ = function (cell) {
  509 + return new google.maps.LatLngBounds(
  510 + new google.maps.LatLng(this.latGrid_[cell[1]], this.lngGrid_[cell[0]]),
  511 + new google.maps.LatLng(this.latGrid_[cell[1] + 1], this.lngGrid_[cell[0] + 1]));
  512 +};
  513 +
  514 +
  515 +RouteBoxer.prototype.initGoogle = function() {
  516 + /* Based on the Latitude/longitude spherical geodesy formulae & scripts
  517 + at http://www.movable-type.co.uk/scripts/latlong.html
  518 + (c) Chris Veness 2002-2010
  519 + */
  520 + google.maps.LatLng.prototype.rhumbDestinationPoint = function (brng, dist) {
  521 + var R = 6371; // earth's mean radius in km
  522 + var d = parseFloat(dist) / R; // d = angular distance covered on earth's surface
  523 + var lat1 = this.lat().toRad(), lon1 = this.lng().toRad();
  524 + brng = brng.toRad();
  525 +
  526 + var lat2 = lat1 + d * Math.cos(brng);
  527 + var dLat = lat2 - lat1;
  528 + var dPhi = Math.log(Math.tan(lat2 / 2 + Math.PI / 4) / Math.tan(lat1 / 2 + Math.PI / 4));
  529 + var q = (Math.abs(dLat) > 1e-10) ? dLat / dPhi : Math.cos(lat1);
  530 + var dLon = d * Math.sin(brng) / q;
  531 + // check for going past the pole
  532 + if (Math.abs(lat2) > Math.PI / 2) {
  533 + lat2 = lat2 > 0 ? Math.PI - lat2 : - (Math.PI - lat2);
  534 + }
  535 + var lon2 = (lon1 + dLon + Math.PI) % (2 * Math.PI) - Math.PI;
  536 +
  537 + if (isNaN(lat2) || isNaN(lon2)) {
  538 + return null;
  539 + }
  540 + return new google.maps.LatLng(lat2.toDeg(), lon2.toDeg());
  541 + };
  542 +
  543 + google.maps.LatLng.prototype.rhumbBearingTo = function (dest) {
  544 + var dLon = (dest.lng() - this.lng()).toRad();
  545 + var dPhi = Math.log(Math.tan(dest.lat().toRad() / 2 + Math.PI / 4) / Math.tan(this.lat().toRad() / 2 + Math.PI / 4));
  546 + if (Math.abs(dLon) > Math.PI) {
  547 + dLon = dLon > 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon);
  548 + }
  549 + return Math.atan2(dLon, dPhi).toBrng();
  550 + };
  551 +
  552 +};
  553 +
  554 +/**
  555 + * Extend the Number object to convert degrees to radians
  556 + *
  557 + * @return {Number} Bearing in radians
  558 + * @ignore
  559 + */
  560 +Number.prototype.toRad = function () {
  561 + return this * Math.PI / 180;
  562 +};
  563 +
  564 +/**
  565 + * Extend the Number object to convert radians to degrees
  566 + *
  567 + * @return {Number} Bearing in degrees
  568 + * @ignore
  569 + */
  570 +Number.prototype.toDeg = function () {
  571 + return this * 180 / Math.PI;
  572 +};
  573 +
  574 +/**
  575 + * Normalize a heading in degrees to between 0 and +360
  576 + *
  577 + * @return {Number} Return
  578 + * @ignore
  579 + */
  580 +Number.prototype.toBrng = function () {
  581 + return (this.toDeg() + 360) % 360;
  582 +};
0 583 \ No newline at end of file
public/stylesheets/main.css View file @ 77ade48
  1 +html, body {
  2 + height: 100%;
  3 + width: 100%;
  4 +}
  5 +
1 6 #alt-routes {
2 7 height: 75px;
3 8 }
4 9  
5 10 #map {
6 11 height: 600px;
7   - margin-bottom: 10px;
  12 +}
  13 +
  14 +#map-area {
  15 + height: 600px;
8 16 width: 100%;
9 17 }
10 18  
... ... @@ -12,6 +20,13 @@
12 20 margin-bottom: 30px;
13 21 }
14 22  
  23 +#right-panel {
  24 + height: 600px;
  25 + /*float: right;
  26 + width: 40%;*/
  27 + overflow: auto;
  28 +}
  29 +
15 30 .img-responsive {
16 31 margin: 0 auto;
17 32 }
... ... @@ -37,4 +52,29 @@
37 52  
38 53 .log-in-textfield{
39 54 width: 30%;
40   -}
41 55 \ No newline at end of file
  56 +}
  57 +
  58 +#floating-panel {
  59 + position: absolute;
  60 + top: 10px;
  61 + left: 25%;
  62 + z-index: 5;
  63 + background-color: #fff;
  64 + padding: 5px;
  65 + border: 1px solid #999;
  66 + text-align: center;
  67 + font-family: 'Roboto','sans-serif';
  68 + line-height: 30px;
  69 + padding-left: 10px;
  70 +}
  71 +#floating-panel {
  72 + background-color: #fff;
  73 + border: 1px solid #999;
  74 + left: 25%;
  75 + padding: 5px;
  76 + position: absolute;
  77 + top: 10px;
  78 + z-index: 5;
  79 +}
  80 +/**
  81 +*/
42 82 \ No newline at end of file
views/pages/index.ejs View file @ 77ade48
... ... @@ -78,7 +78,14 @@
78 78 var place = autoOrigin.getPlace();
79 79 // Testing.
80 80 console.log(place);
  81 +<<<<<<< HEAD
  82 + //localStorage.setItem("from", place.formatted_address);
  83 + localStorage.setItem("fromIn", temp.value);
  84 + localStorage.setItem("originLat", place.geometry.location.lat());
  85 + localStorage.setItem("originLng", place.geometry.location.lng());
  86 +=======
81 87 localStorage.setItem("from", place.formatted_address);
  88 +>>>>>>> 044c2fcc67b88e2e59ea2720d1eb8ababb014660
82 89 });
83 90 }
84 91  
... ... @@ -91,13 +98,48 @@
91 98 var place = autoDest.getPlace();
92 99 // Testing.
93 100 console.log(place);
  101 +<<<<<<< HEAD
  102 + //localStorage.setItem("to", place.formatted_address);
  103 + localStorage.setItem("toIn", temp.value);
  104 + localStorage.setItem("destLat", place.geometry.location.lat());
  105 + localStorage.setItem("destLng", place.geometry.location.lng());
  106 + });
  107 + }
  108 +
  109 + if (navigator.geolocation) {
  110 + navigator.geolocation.getCurrentPosition(function(pos) {
  111 + var geocoder = new google.maps.Geocoder;
  112 + var point = new google.maps.LatLng(
  113 + pos.coords.latitude, pos.coords.longitude);
  114 +
  115 + localStorage.setItem("originLat", pos.coords.latitude);
  116 + localStorage.setItem("originLng", pos.coords.longitude);
  117 +
  118 + geocoder.geocode({'latLng': point}, function (locations, status) {
  119 + if (status == google.maps.GeocoderStatus.OK) {
  120 + for (var location of locations) {
  121 + if ($.inArray("street_address", location.types) != -1) {
  122 + console.log('Your location is: ' + location.formatted_address);
  123 + document.getElementById("origin").value = location.formatted_address;
  124 + saveFrom();
  125 + break;
  126 + }
  127 + };
  128 + }
  129 + });
  130 +=======
94 131 localStorage.setItem("to", place.formatted_address);
  132 +>>>>>>> 044c2fcc67b88e2e59ea2720d1eb8ababb014660
95 133 });
96 134 }
97 135  
98 136 function saveFrom() {
99 137 var fromInput = document.getElementById("origin").value;
100 138 localStorage.setItem("from", fromInput);
  139 +<<<<<<< HEAD
  140 + localStorage.setItem("fromIn", fromInput);
  141 +=======
  142 +>>>>>>> 044c2fcc67b88e2e59ea2720d1eb8ababb014660
101 143 }
102 144 function saveTo() {
103 145 var toInput = document.getElementById("dest").value;
views/pages/landing.ejs View file @ 77ade48
... ... @@ -6,6 +6,15 @@
6 6 src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDAXDHeZXEv4H4ZnThVDxpyAuxVpzOcj_U&libraries=visualization">
7 7 </script> -->
8 8  
  9 + <script>
  10 + window.onload = function() {
  11 + //Deep link URL for existing users with app already installed on their device
  12 +
  13 + //<!-- Download URL (MAT link) for new users to download the app -->
  14 + // setTimeout("window.location = 'http://hastrk.com/serve?action=click&publisher_id=1&site_id=2';", 1000);
  15 + }
  16 + </script>
  17 +
9 18 </head>
10 19  
11 20  
... ... @@ -14,11 +23,11 @@
14 23  
15 24 <!-- My LOCATION -->
16 25 <div>
17   - <input type="text" class="form-control text-field" id="origin" name="fromText" placeholder="MY LOCATION" onclick="initFrom()">
  26 + <input type="text" class="form-control text-field" id="origin" placeholder="My Location" onclick="initFrom()">
18 27 </div>
19 28 <!-- Destination -->
20 29 <div>
21   - <input type="text" class="form-control text-field" id="dest" name="toText" placeholder="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna..." onclick="initTo()">
  30 + <input type="text" class="form-control text-field" id="dest" placeholder="Final Destination" onclick="initTo()">
22 31 </div>
23 32  
24 33 <!-- Buttons and status information on top of the map. -->
... ... @@ -28,29 +37,56 @@
28 37 <div class="col-xs-6">
29 38 <!-- Title: Current Route -->
30 39 <div>
31   - Current Route:
  40 + Current Route Safety:
32 41 </div>
33 42 <!-- Icons + Status -->
34 43 <div class="row status">
35 44 <!-- Safety Rating -->
36   - <div class="col-xs-6">
37   - 80% Safe
  45 + <div class="col-xs-4" name="grade">
  46 +
38 47 </div>
  48 + </div>
  49 + </div>
  50 +
  51 + <div class="col-xs-6">
  52 + <div>
  53 + Estimated Route Time:
  54 + </div>
  55 + <div class="row status">
39 56 <!-- Travel Duration -->
40   - <div class="col-xs-6">
41   - 10 Mins
  57 + <div class="col-xs-4" name="time">
42 58 </div>
43 59 </div>
44 60 </div>
45   - <!-- Alt Routes -->
46   - <div class="col-xs-6">
47   - <button type="button" class="btn btn-default btn-block" id="alt-routes" onclick="todo()">Alternate Routes</button>
  61 +
  62 +
  63 + <!-- Alt Routes
  64 + <div class="col-xs-4">
  65 + <button type="button" class="btn btn-default btn-block text-center" id="alt-routes" onclick="todo()">Alternate Routes</button>
48 66 </div>
  67 + -->
49 68 </div>
50 69 </div>
51 70  
52   - <!-- Div container for map. -->
53   - <div id="map"></div>
  71 + <!-- Floating panel for key or for toggle heat map
  72 + <div id="floating-panel">
  73 + <button onclick="toggleHeatmap()">Toggle Heatmap</button>
  74 + <button onclick="changeGradient()">Change gradient</button>
  75 + <button onclick="changeRadius()">Change radius</button>
  76 + <button onclick="changeOpacity()">Change opacity</button>
  77 + </div>
  78 + -->
  79 +
  80 + <div> Move a circle to get a crime summary of that location (half mile radius)</div>
  81 +
  82 + <dir class="container" id="map-area">
  83 + <div class="row">
  84 + <!-- Div container for map. -->
  85 + <div class="col-xs-7" id="map"></div>
  86 + <!-- Div container for the directions display-->
  87 + <div class="col-xs-5" id="right-panel"></div>
  88 + </div>
  89 + </dir>
54 90  
55 91 <!-- Javascript for the map. -->
56 92 <script>
... ... @@ -58,8 +94,11 @@
58 94 // Used by the Google Maps Direction API.
59 95 var directionsService;
60 96 var directionsDisplay;
  97 + //array to hold the circle added to map
  98 + var circleArray;
61 99  
62 100 function initMap() {
  101 + circleArray = [];
63 102 directionsService = new google.maps.DirectionsService;
64 103 directionsDisplay = new google.maps.DirectionsRenderer;
65 104 // Setting default map location i.e. Geisel Library.
... ... @@ -67,13 +106,8 @@
67 106 zoom: 13,
68 107 center: {lat: 37.775, lng: -122.434}
69 108 });
70   -
71   - heatmap = new google.maps.visualization.HeatmapLayer({
72   - data: getPoints(),
73   - map: map
74   - });
75 109 directionsDisplay.setMap(map);
76   -
  110 + directionsDisplay.setPanel(document.getElementById('right-panel'));
77 111 // Keeping the map center when the browser is resized.
78 112 function resizing() {
79 113 var center = map.getCenter();
... ... @@ -82,15 +116,14 @@
82 116 }
83 117  
84 118 google.maps.event.addDomListener(window, "resize", resizing);
85   -
86   - // // Showing route.
  119 + // Show route.
87 120 displayRoute(directionsService, directionsDisplay);
88 121 }
89 122  
90 123 function displayRoute(directionsService, directionsDisplay) {
91 124 // Textfields that show from and to.
92   - document.getElementsByName("fromText")[0].placeholder=localStorage.getItem("from");
93   - document.getElementsByName("toText")[0].placeholder=localStorage.getItem("to");
  125 + document.getElementById("origin").value = localStorage.getItem("fromIn");
  126 + document.getElementById("dest").value = localStorage.getItem("toIn");
94 127  
95 128 // Default mode to driving if none was chosen.
96 129 var mode = localStorage.getItem("mode");
... ... @@ -99,15 +132,22 @@
99 132 mode = "DRIVING";
100 133 }
101 134  
  135 + var originPoint = new google.maps.LatLng(localStorage.getItem("originLat"), localStorage.getItem("originLng"));
  136 + var destPoint = new google.maps.LatLng(localStorage.getItem("destLat"), localStorage.getItem("destLng"));
  137 +
102 138 var request = {
103   - origin: localStorage.getItem("from"),
104   - destination: localStorage.getItem("to"),
  139 + origin: originPoint,
  140 + destination: destPoint,
105 141 travelMode: mode
106 142 };
107 143  
108 144 directionsService.route(request, function(response, status) {
109 145 if (status === "OK") {
  146 + console.log(response);
110 147 directionsDisplay.setDirections(response);
  148 + document.getElementsByName("time")[0].innerHTML = response.routes[0].legs[0].duration.text
  149 + //Set up the markers to show danger in different areas
  150 + getPoints(response.routes[0]);
111 151 }
112 152 else {
113 153 window.alert("Directions request failed due to " + status);
... ... @@ -117,550 +157,198 @@
117 157  
118 158 function initFrom() {
119 159 var temp = document.getElementById("origin");
120   -
121 160 var autoOrigin = new google.maps.places.Autocomplete(temp);
122   -
123 161 google.maps.event.addListener(autoOrigin, 'place_changed', function () {
124 162 var place = autoOrigin.getPlace();
125 163 // Testing.
126 164 console.log(place);
127   - localStorage.setItem("from", place.formatted_address);
  165 + localStorage.setItem("fromIn", temp.value);
  166 + localStorage.setItem("originLat", place.geometry.location.lat());
  167 + localStorage.setItem("originLng", place.geometry.location.lng());
  168 + //Prevent memory leak, remove circle from last route
  169 + removeCircles();
  170 + clearGrade();
128 171 displayRoute(directionsService, directionsDisplay);
129 172 });
130 173 }
131 174  
132 175 function initTo() {
133 176 var temp = document.getElementById("dest");
134   -
135 177 var autoDest = new google.maps.places.Autocomplete(temp);
136   -
137 178 google.maps.event.addListener(autoDest, 'place_changed', function () {
138 179 var place = autoDest.getPlace();
139 180 // Testing.
140 181 console.log(place);
141   - localStorage.setItem("to", place.formatted_address);
  182 + localStorage.setItem("toIn", temp.value);
  183 + localStorage.setItem("destLat", place.geometry.location.lat());
  184 + localStorage.setItem("destLng", place.geometry.location.lng());
  185 + //Prevent memory leak, remove circle from last route
  186 + removeCircles();
  187 + clearGrade();
142 188 displayRoute(directionsService, directionsDisplay);
143 189 });
144 190 }
145 191  
146   - function getPoints() {
147   - //read json here from passed in parameters
148   - //loop here that loops through the passed in json values for points along the route
149   - //return the list of objects created from those longitudes and latitudes
  192 + function getPoints(route) {
  193 + //set up HTTP client to make GET request to API
  194 + var HttpClient = function() {
  195 + this.get = function(aUrl, aCallback) {
  196 + var anHttpRequest = new XMLHttpRequest();
  197 + anHttpRequest.onreadystatechange = function() {
  198 + if (anHttpRequest.readyState == 4 && anHttpRequest.status == 200)
  199 + aCallback(anHttpRequest.responseText);
  200 + }
  201 + //gj4g6D3zxKmshys7yyf9KNbuywOxp1ueazFjsn6n2fK4sccIGA
  202 + anHttpRequest.open( "GET", aUrl, true );
  203 + anHttpRequest.setRequestHeader("X-Mashape-Key", "W4MVkGfi3ZmshbMN0LqORTZunzeTp1rEG5Pjsn9og1t7bRnQxG");
  204 + //W4MVkGfi3ZmshbMN0LqORTZunzeTp1rEG5Pjsn9og1t7bRnQxG
  205 + anHttpRequest.setRequestHeader("Accept", "application/json")
  206 + anHttpRequest.send( null );
  207 + }
  208 + }
150 209  
151   - //THIS IS THE CODE TO READ JSON BUT IT'S NOT VIABLE TO CREATE ALL THESE POINTS MANUALLY IN A JSON FILE.
152   - //So instead we copy pasted a sample set of points to demonstrate the functionality
  210 + //Loop through the steps of the route to get the center point
  211 + var bounds = new google.maps.LatLngBounds();
  212 + var legs = route.legs;
  213 + for (i=0;i<legs.length;i++) {
  214 + var steps = legs[i].steps;
  215 + for (j=0;j<steps.length;j++) {
  216 + var nextSegment = steps[j].path;
  217 + for (k=0;k<nextSegment.length;k++) {
  218 + // polyline.getPath().push(nextSegment[k]);
  219 + bounds.extend(nextSegment[k]);
  220 + }
  221 + }
  222 + }
153 223  
154   - /*var toReturn = [];
155   - for (i = 0; i < [insert ejs symbols here]spots.length[insert ejs symbols here]; i++) {
156   - toReturn.push(new google.maps.LatLng(insert ejs symbols here spots[i].latutudeinsert ejs symbols here, insert ejs symbols here spots[i].longitudeinsert ejs symbols here));
  224 + //create instance of HTTP client
  225 + aClient = new HttpClient();
  226 + /*Also very possible to use RouteBoxer as a better solution*/
  227 + // for (var i = route.legs[0].steps.length - 1; i >= 0; i--) {
  228 +
  229 + for (var i = 0; i < 3; i++){
  230 + var long = route.legs[0].steps[i].end_point.lng();
  231 + var lat = route.legs[0].steps[i].end_point.lat();
  232 + if(i == 2){
  233 + long = route.legs[0].steps[route.legs[0].steps.length - 1].end_point.lng();
  234 + lat = route.legs[0].steps[route.legs[0].steps.length - 1].end_point.lat();
  235 + }
  236 +
  237 + //If middle point then get the center point from bounds
  238 + else if(i == 1){
  239 + long = bounds.getCenter().lng();
  240 + lat = bounds.getCenter().lat();
  241 + }
  242 + //Base string concatenated with GET request options
  243 + var str = "https://crimescore.p.mashape.com/crimescore?f=json&id=174&lat=".concat(lat).concat("&lon=").concat(long);
  244 + //Make the HTTP request
  245 + aClient.get(str, function(result) {
  246 + //check what the score is
  247 + var object = JSON.parse(result);
  248 + var score = parseInt(object.score);
  249 + var grade = object.grade;
  250 + console.log(result);
  251 +
  252 +
  253 + //Colors:
  254 + //Yellow: #f1c40f
  255 + //Red: #e74c3c
  256 + //Light Green: #2ecc71
  257 + //Dark Green: #27ae60
  258 + //Blue: #3498db
  259 +
  260 + //create the options object
  261 + var circle = new google.maps.Circle({
  262 + strokeColor: '#3498db',
  263 + strokeOpacity: 0.8,
  264 + strokeWeight: 2,
  265 + fillColor: '#3498db',
  266 + fillOpacity: 0.35,
  267 + map: map,
  268 + center: {lat: parseFloat(object.latitude), lng: parseFloat(object.longitude)},
  269 + radius: 804,
  270 + draggable: true
  271 + })
  272 +
  273 + setColor(score, circle);
  274 + setGrade(object.grade);
  275 + circleArray.push(circle);
  276 + circle.addListener('dragend', function(event){
  277 + var newLat = event.latLng.lat();
  278 + var newLong = event.latLng.lng();
  279 +
  280 + var newStr = "https://crimescore.p.mashape.com/crimescore?f=json&id=174&lat=".concat(newLat).concat("&lon=").concat(newLong)
  281 + aClient.get(newStr, function(result){
  282 + //change the color
  283 + var newObject = JSON.parse(result);
  284 + var newScore = parseInt(newObject.score);
  285 + setColor(newScore, circle);
  286 + // setGrade(object.grade);
  287 + });
  288 + });
  289 + });
157 290 }
  291 + }
158 292  
159   - return toReturn;*/
160   -
161   -
162   - return [
163   - new google.maps.LatLng(37.782551, -122.445368),
164   - new google.maps.LatLng(37.782745, -122.444586),
165   - new google.maps.LatLng(37.782842, -122.443688),
166   - new google.maps.LatLng(37.782919, -122.442815),
167   - new google.maps.LatLng(37.782992, -122.442112),
168   - new google.maps.LatLng(37.783100, -122.441461),
169   - new google.maps.LatLng(37.783206, -122.440829),
170   - new google.maps.LatLng(37.783273, -122.440324),
171   - new google.maps.LatLng(37.783316, -122.440023),
172   - new google.maps.LatLng(37.783357, -122.439794),
173   - new google.maps.LatLng(37.783371, -122.439687),
174   - new google.maps.LatLng(37.783368, -122.439666),
175   - new google.maps.LatLng(37.783383, -122.439594),
176   - new google.maps.LatLng(37.783508, -122.439525),
177   - new google.maps.LatLng(37.783842, -122.439591),
178   - new google.maps.LatLng(37.784147, -122.439668),
179   - new google.maps.LatLng(37.784206, -122.439686),
180   - new google.maps.LatLng(37.784386, -122.439790),
181   - new google.maps.LatLng(37.784701, -122.439902),
182   - new google.maps.LatLng(37.784965, -122.439938),
183   - new google.maps.LatLng(37.785010, -122.439947),
184   - new google.maps.LatLng(37.785360, -122.439952),
185   - new google.maps.LatLng(37.785715, -122.440030),
186   - new google.maps.LatLng(37.786117, -122.440119),
187   - new google.maps.LatLng(37.786564, -122.440209),
188   - new google.maps.LatLng(37.786905, -122.440270),
189   - new google.maps.LatLng(37.786956, -122.440279),
190   - new google.maps.LatLng(37.800224, -122.433520),
191   - new google.maps.LatLng(37.800155, -122.434101),
192   - new google.maps.LatLng(37.800160, -122.434430),
193   - new google.maps.LatLng(37.800378, -122.434527),
194   - new google.maps.LatLng(37.800738, -122.434598),
195   - new google.maps.LatLng(37.800938, -122.434650),
196   - new google.maps.LatLng(37.801024, -122.434889),
197   - new google.maps.LatLng(37.800955, -122.435392),
198   - new google.maps.LatLng(37.800886, -122.435959),
199   - new google.maps.LatLng(37.800811, -122.436275),
200   - new google.maps.LatLng(37.800788, -122.436299),
201   - new google.maps.LatLng(37.800719, -122.436302),
202   - new google.maps.LatLng(37.800702, -122.436298),
203   - new google.maps.LatLng(37.800661, -122.436273),
204   - new google.maps.LatLng(37.800395, -122.436172),
205   - new google.maps.LatLng(37.800228, -122.436116),
206   - new google.maps.LatLng(37.800169, -122.436130),
207   - new google.maps.LatLng(37.800066, -122.436167),
208   - new google.maps.LatLng(37.784345, -122.422922),
209   - new google.maps.LatLng(37.784389, -122.422926),
210   - new google.maps.LatLng(37.784437, -122.422924),
211   - new google.maps.LatLng(37.784746, -122.422818),
212   - new google.maps.LatLng(37.785436, -122.422959),
213   - new google.maps.LatLng(37.786120, -122.423112),
214   - new google.maps.LatLng(37.786433, -122.423029),
215   - new google.maps.LatLng(37.786631, -122.421213),
216   - new google.maps.LatLng(37.786660, -122.421033),
217   - new google.maps.LatLng(37.786801, -122.420141),
218   - new google.maps.LatLng(37.786823, -122.420034),
219   - new google.maps.LatLng(37.786831, -122.419916),
220   - new google.maps.LatLng(37.787034, -122.418208),
221   - new google.maps.LatLng(37.787056, -122.418034),
222   - new google.maps.LatLng(37.787169, -122.417145),
223   - new google.maps.LatLng(37.787217, -122.416715),
224   - new google.maps.LatLng(37.786144, -122.416403),
225   - new google.maps.LatLng(37.785292, -122.416257),
226   - new google.maps.LatLng(37.780666, -122.390374),
227   - new google.maps.LatLng(37.780501, -122.391281),
228   - new google.maps.LatLng(37.780148, -122.392052),
229   - new google.maps.LatLng(37.780173, -122.391148),
230   - new google.maps.LatLng(37.780693, -122.390592),
231   - new google.maps.LatLng(37.781261, -122.391142),
232   - new google.maps.LatLng(37.781808, -122.391730),
233   - new google.maps.LatLng(37.782340, -122.392341),
234   - new google.maps.LatLng(37.782812, -122.393022),
235   - new google.maps.LatLng(37.783300, -122.393672),
236   - new google.maps.LatLng(37.783809, -122.394275),
237   - new google.maps.LatLng(37.784246, -122.394979),
238   - new google.maps.LatLng(37.784791, -122.395958),
239   - new google.maps.LatLng(37.785675, -122.396746),
240   - new google.maps.LatLng(37.786262, -122.395780),
241   - new google.maps.LatLng(37.786776, -122.395093),
242   - new google.maps.LatLng(37.787282, -122.394426),
243   - new google.maps.LatLng(37.787783, -122.393767),
244   - new google.maps.LatLng(37.788343, -122.393184),
245   - new google.maps.LatLng(37.788895, -122.392506),
246   - new google.maps.LatLng(37.789371, -122.391701),
247   - new google.maps.LatLng(37.789722, -122.390952),
248   - new google.maps.LatLng(37.790315, -122.390305),
249   - new google.maps.LatLng(37.790738, -122.389616),
250   - new google.maps.LatLng(37.779448, -122.438702),
251   - new google.maps.LatLng(37.779023, -122.438585),
252   - new google.maps.LatLng(37.778542, -122.438492),
253   - new google.maps.LatLng(37.778100, -122.438411),
254   - new google.maps.LatLng(37.777986, -122.438376),
255   - new google.maps.LatLng(37.777680, -122.438313),
256   - new google.maps.LatLng(37.777316, -122.438273),
257   - new google.maps.LatLng(37.777135, -122.438254),
258   - new google.maps.LatLng(37.776987, -122.438303),
259   - new google.maps.LatLng(37.776946, -122.438404),
260   - new google.maps.LatLng(37.776944, -122.438467),
261   - new google.maps.LatLng(37.776892, -122.438459),
262   - new google.maps.LatLng(37.776842, -122.438442),
263   - new google.maps.LatLng(37.776822, -122.438391),
264   - new google.maps.LatLng(37.776814, -122.438412),
265   - new google.maps.LatLng(37.776787, -122.438628),
266   - new google.maps.LatLng(37.776729, -122.438650),
267   - new google.maps.LatLng(37.776759, -122.438677),
268   - new google.maps.LatLng(37.776772, -122.438498),
269   - new google.maps.LatLng(37.776787, -122.438389),
270   - new google.maps.LatLng(37.776848, -122.438283),
271   - new google.maps.LatLng(37.776870, -122.438239),
272   - new google.maps.LatLng(37.777015, -122.438198),
273   - new google.maps.LatLng(37.777333, -122.438256),
274   - new google.maps.LatLng(37.777595, -122.438308),
275   - new google.maps.LatLng(37.777797, -122.438344),
276   - new google.maps.LatLng(37.778160, -122.438442),
277   - new google.maps.LatLng(37.778414, -122.438508),
278   - new google.maps.LatLng(37.778445, -122.438516),
279   - new google.maps.LatLng(37.778503, -122.438529),
280   - new google.maps.LatLng(37.778607, -122.438549),
281   - new google.maps.LatLng(37.778670, -122.438644),
282   - new google.maps.LatLng(37.778847, -122.438706),
283   - new google.maps.LatLng(37.779240, -122.438744),
284   - new google.maps.LatLng(37.779738, -122.438822),
285   - new google.maps.LatLng(37.780201, -122.438882),
286   - new google.maps.LatLng(37.780400, -122.438905),
287   - new google.maps.LatLng(37.780501, -122.438921),
288   - new google.maps.LatLng(37.780892, -122.438986),
289   - new google.maps.LatLng(37.781446, -122.439087),
290   - new google.maps.LatLng(37.781985, -122.439199),
291   - new google.maps.LatLng(37.782239, -122.439249),
292   - new google.maps.LatLng(37.782286, -122.439266),
293   - new google.maps.LatLng(37.797847, -122.429388),
294   - new google.maps.LatLng(37.797874, -122.429180),
295   - new google.maps.LatLng(37.797885, -122.429069),
296   - new google.maps.LatLng(37.797887, -122.429050),
297   - new google.maps.LatLng(37.797933, -122.428954),
298   - new google.maps.LatLng(37.798242, -122.428990),
299   - new google.maps.LatLng(37.798617, -122.429075),
300   - new google.maps.LatLng(37.798719, -122.429092),
301   - new google.maps.LatLng(37.798944, -122.429145),
302   - new google.maps.LatLng(37.799320, -122.429251),
303   - new google.maps.LatLng(37.799590, -122.429309),
304   - new google.maps.LatLng(37.799677, -122.429324),
305   - new google.maps.LatLng(37.799966, -122.429360),
306   - new google.maps.LatLng(37.800288, -122.429430),
307   - new google.maps.LatLng(37.800443, -122.429461),
308   - new google.maps.LatLng(37.800465, -122.429474),
309   - new google.maps.LatLng(37.800644, -122.429540),
310   - new google.maps.LatLng(37.800948, -122.429620),
311   - new google.maps.LatLng(37.801242, -122.429685),
312   - new google.maps.LatLng(37.801375, -122.429702),
313   - new google.maps.LatLng(37.801400, -122.429703),
314   - new google.maps.LatLng(37.801453, -122.429707),
315   - new google.maps.LatLng(37.801473, -122.429709),
316   - new google.maps.LatLng(37.801532, -122.429707),
317   - new google.maps.LatLng(37.801852, -122.429729),
318   - new google.maps.LatLng(37.802173, -122.429789),
319   - new google.maps.LatLng(37.802459, -122.429847),
320   - new google.maps.LatLng(37.802554, -122.429825),
321   - new google.maps.LatLng(37.802647, -122.429549),
322   - new google.maps.LatLng(37.802693, -122.429179),
323   - new google.maps.LatLng(37.802729, -122.428751),
324   - new google.maps.LatLng(37.766104, -122.409291),
325   - new google.maps.LatLng(37.766103, -122.409268),
326   - new google.maps.LatLng(37.766138, -122.409229),
327   - new google.maps.LatLng(37.766183, -122.409231),
328   - new google.maps.LatLng(37.766153, -122.409276),
329   - new google.maps.LatLng(37.766005, -122.409365),
330   - new google.maps.LatLng(37.765897, -122.409570),
331   - new google.maps.LatLng(37.765767, -122.409739),
332   - new google.maps.LatLng(37.765693, -122.410389),
333   - new google.maps.LatLng(37.765615, -122.411201),
334   - new google.maps.LatLng(37.765533, -122.412121),
335   - new google.maps.LatLng(37.765467, -122.412939),
336   - new google.maps.LatLng(37.765444, -122.414821),
337   - new google.maps.LatLng(37.765444, -122.414964),
338   - new google.maps.LatLng(37.765318, -122.415424),
339   - new google.maps.LatLng(37.763961, -122.415296),
340   - new google.maps.LatLng(37.763115, -122.415196),
341   - new google.maps.LatLng(37.762967, -122.415183),
342   - new google.maps.LatLng(37.762278, -122.415127),
343   - new google.maps.LatLng(37.761675, -122.415055),
344   - new google.maps.LatLng(37.760932, -122.414988),
345   - new google.maps.LatLng(37.759337, -122.414862),
346   - new google.maps.LatLng(37.773187, -122.421922),
347   - new google.maps.LatLng(37.773043, -122.422118),
348   - new google.maps.LatLng(37.773007, -122.422165),
349   - new google.maps.LatLng(37.772979, -122.422219),
350   - new google.maps.LatLng(37.772865, -122.422394),
351   - new google.maps.LatLng(37.772779, -122.422503),
352   - new google.maps.LatLng(37.772676, -122.422701),
353   - new google.maps.LatLng(37.772606, -122.422806),
354   - new google.maps.LatLng(37.772566, -122.422840),
355   - new google.maps.LatLng(37.772508, -122.422852),
356   - new google.maps.LatLng(37.772387, -122.423011),
357   - new google.maps.LatLng(37.772099, -122.423328),
358   - new google.maps.LatLng(37.771704, -122.423783),
359   - new google.maps.LatLng(37.771481, -122.424081),
360   - new google.maps.LatLng(37.771400, -122.424179),
361   - new google.maps.LatLng(37.771352, -122.424220),
362   - new google.maps.LatLng(37.771248, -122.424327),
363   - new google.maps.LatLng(37.770904, -122.424781),
364   - new google.maps.LatLng(37.770520, -122.425283),
365   - new google.maps.LatLng(37.770337, -122.425553),
366   - new google.maps.LatLng(37.770128, -122.425832),
367   - new google.maps.LatLng(37.769756, -122.426331),
368   - new google.maps.LatLng(37.769300, -122.426902),
369   - new google.maps.LatLng(37.769132, -122.427065),
370   - new google.maps.LatLng(37.769092, -122.427103),
371   - new google.maps.LatLng(37.768979, -122.427172),
372   - new google.maps.LatLng(37.768595, -122.427634),
373   - new google.maps.LatLng(37.768372, -122.427913),
374   - new google.maps.LatLng(37.768337, -122.427961),
375   - new google.maps.LatLng(37.768244, -122.428138),
376   - new google.maps.LatLng(37.767942, -122.428581),
377   - new google.maps.LatLng(37.767482, -122.429094),
378   - new google.maps.LatLng(37.767031, -122.429606),
379   - new google.maps.LatLng(37.766732, -122.429986),
380   - new google.maps.LatLng(37.766680, -122.430058),
381   - new google.maps.LatLng(37.766633, -122.430109),
382   - new google.maps.LatLng(37.766580, -122.430211),
383   - new google.maps.LatLng(37.766367, -122.430594),
384   - new google.maps.LatLng(37.765910, -122.431137),
385   - new google.maps.LatLng(37.765353, -122.431806),
386   - new google.maps.LatLng(37.764962, -122.432298),
387   - new google.maps.LatLng(37.764868, -122.432486),
388   - new google.maps.LatLng(37.764518, -122.432913),
389   - new google.maps.LatLng(37.763435, -122.434173),
390   - new google.maps.LatLng(37.762847, -122.434953),
391   - new google.maps.LatLng(37.762291, -122.435935),
392   - new google.maps.LatLng(37.762224, -122.436074),
393   - new google.maps.LatLng(37.761957, -122.436892),
394   - new google.maps.LatLng(37.761652, -122.438886),
395   - new google.maps.LatLng(37.761284, -122.439955),
396   - new google.maps.LatLng(37.761210, -122.440068),
397   - new google.maps.LatLng(37.761064, -122.440720),
398   - new google.maps.LatLng(37.761040, -122.441411),
399   - new google.maps.LatLng(37.761048, -122.442324),
400   - new google.maps.LatLng(37.760851, -122.443118),
401   - new google.maps.LatLng(37.759977, -122.444591),
402   - new google.maps.LatLng(37.759913, -122.444698),
403   - new google.maps.LatLng(37.759623, -122.445065),
404   - new google.maps.LatLng(37.758902, -122.445158),
405   - new google.maps.LatLng(37.758428, -122.444570),
406   - new google.maps.LatLng(37.757687, -122.443340),
407   - new google.maps.LatLng(37.757583, -122.443240),
408   - new google.maps.LatLng(37.757019, -122.442787),
409   - new google.maps.LatLng(37.756603, -122.442322),
410   - new google.maps.LatLng(37.756380, -122.441602),
411   - new google.maps.LatLng(37.755790, -122.441382),
412   - new google.maps.LatLng(37.754493, -122.442133),
413   - new google.maps.LatLng(37.754361, -122.442206),
414   - new google.maps.LatLng(37.753719, -122.442650),
415   - new google.maps.LatLng(37.753096, -122.442915),
416   - new google.maps.LatLng(37.751617, -122.443211),
417   - new google.maps.LatLng(37.751496, -122.443246),
418   - new google.maps.LatLng(37.750733, -122.443428),
419   - new google.maps.LatLng(37.750126, -122.443536),
420   - new google.maps.LatLng(37.750103, -122.443784),
421   - new google.maps.LatLng(37.750390, -122.444010),
422   - new google.maps.LatLng(37.750448, -122.444013),
423   - new google.maps.LatLng(37.750536, -122.444040),
424   - new google.maps.LatLng(37.750493, -122.444141),
425   - new google.maps.LatLng(37.790859, -122.402808),
426   - new google.maps.LatLng(37.790864, -122.402768),
427   - new google.maps.LatLng(37.790995, -122.402539),
428   - new google.maps.LatLng(37.791148, -122.402172),
429   - new google.maps.LatLng(37.791385, -122.401312),
430   - new google.maps.LatLng(37.791405, -122.400776),
431   - new google.maps.LatLng(37.791288, -122.400528),
432   - new google.maps.LatLng(37.791113, -122.400441),
433   - new google.maps.LatLng(37.791027, -122.400395),
434   - new google.maps.LatLng(37.791094, -122.400311),
435   - new google.maps.LatLng(37.791211, -122.400183),
436   - new google.maps.LatLng(37.791060, -122.399334),
437   - new google.maps.LatLng(37.790538, -122.398718),
438   - new google.maps.LatLng(37.790095, -122.398086),
439   - new google.maps.LatLng(37.789644, -122.397360),
440   - new google.maps.LatLng(37.789254, -122.396844),
441   - new google.maps.LatLng(37.788855, -122.396397),
442   - new google.maps.LatLng(37.788483, -122.395963),
443   - new google.maps.LatLng(37.788015, -122.395365),
444   - new google.maps.LatLng(37.787558, -122.394735),
445   - new google.maps.LatLng(37.787472, -122.394323),
446   - new google.maps.LatLng(37.787630, -122.394025),
447   - new google.maps.LatLng(37.787767, -122.393987),
448   - new google.maps.LatLng(37.787486, -122.394452),
449   - new google.maps.LatLng(37.786977, -122.395043),
450   - new google.maps.LatLng(37.786583, -122.395552),
451   - new google.maps.LatLng(37.786540, -122.395610),
452   - new google.maps.LatLng(37.786516, -122.395659),
453   - new google.maps.LatLng(37.786378, -122.395707),
454   - new google.maps.LatLng(37.786044, -122.395362),
455   - new google.maps.LatLng(37.785598, -122.394715),
456   - new google.maps.LatLng(37.785321, -122.394361),
457   - new google.maps.LatLng(37.785207, -122.394236),
458   - new google.maps.LatLng(37.785751, -122.394062),
459   - new google.maps.LatLng(37.785996, -122.393881),
460   - new google.maps.LatLng(37.786092, -122.393830),
461   - new google.maps.LatLng(37.785998, -122.393899),
462   - new google.maps.LatLng(37.785114, -122.394365),
463   - new google.maps.LatLng(37.785022, -122.394441),
464   - new google.maps.LatLng(37.784823, -122.394635),
465   - new google.maps.LatLng(37.784719, -122.394629),
466   - new google.maps.LatLng(37.785069, -122.394176),
467   - new google.maps.LatLng(37.785500, -122.393650),
468   - new google.maps.LatLng(37.785770, -122.393291),
469   - new google.maps.LatLng(37.785839, -122.393159),
470   - new google.maps.LatLng(37.782651, -122.400628),
471   - new google.maps.LatLng(37.782616, -122.400599),
472   - new google.maps.LatLng(37.782702, -122.400470),
473   - new google.maps.LatLng(37.782915, -122.400192),
474   - new google.maps.LatLng(37.783137, -122.399887),
475   - new google.maps.LatLng(37.783414, -122.399519),
476   - new google.maps.LatLng(37.783629, -122.399237),
477   - new google.maps.LatLng(37.783688, -122.399157),
478   - new google.maps.LatLng(37.783716, -122.399106),
479   - new google.maps.LatLng(37.783798, -122.399072),
480   - new google.maps.LatLng(37.783997, -122.399186),
481   - new google.maps.LatLng(37.784271, -122.399538),
482   - new google.maps.LatLng(37.784577, -122.399948),
483   - new google.maps.LatLng(37.784828, -122.400260),
484   - new google.maps.LatLng(37.784999, -122.400477),
485   - new google.maps.LatLng(37.785113, -122.400651),
486   - new google.maps.LatLng(37.785155, -122.400703),
487   - new google.maps.LatLng(37.785192, -122.400749),
488   - new google.maps.LatLng(37.785278, -122.400839),
489   - new google.maps.LatLng(37.785387, -122.400857),
490   - new google.maps.LatLng(37.785478, -122.400890),
491   - new google.maps.LatLng(37.785526, -122.401022),
492   - new google.maps.LatLng(37.785598, -122.401148),
493   - new google.maps.LatLng(37.785631, -122.401202),
494   - new google.maps.LatLng(37.785660, -122.401267),
495   - new google.maps.LatLng(37.803986, -122.426035),
496   - new google.maps.LatLng(37.804102, -122.425089),
497   - new google.maps.LatLng(37.804211, -122.424156),
498   - new google.maps.LatLng(37.803861, -122.423385),
499   - new google.maps.LatLng(37.803151, -122.423214),
500   - new google.maps.LatLng(37.802439, -122.423077),
501   - new google.maps.LatLng(37.801740, -122.422905),
502   - new google.maps.LatLng(37.801069, -122.422785),
503   - new google.maps.LatLng(37.800345, -122.422649),
504   - new google.maps.LatLng(37.799633, -122.422603),
505   - new google.maps.LatLng(37.799750, -122.421700),
506   - new google.maps.LatLng(37.799885, -122.420854),
507   - new google.maps.LatLng(37.799209, -122.420607),
508   - new google.maps.LatLng(37.795656, -122.400395),
509   - new google.maps.LatLng(37.795203, -122.400304),
510   - new google.maps.LatLng(37.778738, -122.415584),
511   - new google.maps.LatLng(37.778812, -122.415189),
512   - new google.maps.LatLng(37.778824, -122.415092),
513   - new google.maps.LatLng(37.778833, -122.414932),
514   - new google.maps.LatLng(37.778834, -122.414898),
515   - new google.maps.LatLng(37.778740, -122.414757),
516   - new google.maps.LatLng(37.778501, -122.414433),
517   - new google.maps.LatLng(37.778182, -122.414026),
518   - new google.maps.LatLng(37.777851, -122.413623),
519   - new google.maps.LatLng(37.777486, -122.413166),
520   - new google.maps.LatLng(37.777109, -122.412674),
521   - new google.maps.LatLng(37.776743, -122.412186),
522   - new google.maps.LatLng(37.776440, -122.411800),
523   - new google.maps.LatLng(37.776295, -122.411614),
524   - new google.maps.LatLng(37.776158, -122.411440),
525   - new google.maps.LatLng(37.775806, -122.410997),
526   - new google.maps.LatLng(37.775422, -122.410484),
527   - new google.maps.LatLng(37.775126, -122.410087),
528   - new google.maps.LatLng(37.775012, -122.409854),
529   - new google.maps.LatLng(37.775164, -122.409573),
530   - new google.maps.LatLng(37.775498, -122.409180),
531   - new google.maps.LatLng(37.775868, -122.408730),
532   - new google.maps.LatLng(37.776256, -122.408240),
533   - new google.maps.LatLng(37.776519, -122.407928),
534   - new google.maps.LatLng(37.776539, -122.407904),
535   - new google.maps.LatLng(37.776595, -122.407854),
536   - new google.maps.LatLng(37.776853, -122.407547),
537   - new google.maps.LatLng(37.777234, -122.407087),
538   - new google.maps.LatLng(37.777644, -122.406558),
539   - new google.maps.LatLng(37.778066, -122.406017),
540   - new google.maps.LatLng(37.778468, -122.405499),
541   - new google.maps.LatLng(37.778866, -122.404995),
542   - new google.maps.LatLng(37.779295, -122.404455),
543   - new google.maps.LatLng(37.779695, -122.403950),
544   - new google.maps.LatLng(37.779982, -122.403584),
545   - new google.maps.LatLng(37.780295, -122.403223),
546   - new google.maps.LatLng(37.780664, -122.402766),
547   - new google.maps.LatLng(37.781043, -122.402288),
548   - new google.maps.LatLng(37.781399, -122.401823),
549   - new google.maps.LatLng(37.781727, -122.401407),
550   - new google.maps.LatLng(37.781853, -122.401247),
551   - new google.maps.LatLng(37.781894, -122.401195),
552   - new google.maps.LatLng(37.782076, -122.400977),
553   - new google.maps.LatLng(37.782338, -122.400603),
554   - new google.maps.LatLng(37.782666, -122.400133),
555   - new google.maps.LatLng(37.783048, -122.399634),
556   - new google.maps.LatLng(37.783450, -122.399198),
557   - new google.maps.LatLng(37.783791, -122.398998),
558   - new google.maps.LatLng(37.784177, -122.398959),
559   - new google.maps.LatLng(37.784388, -122.398971),
560   - new google.maps.LatLng(37.784404, -122.399128),
561   - new google.maps.LatLng(37.784586, -122.399524),
562   - new google.maps.LatLng(37.784835, -122.399927),
563   - new google.maps.LatLng(37.785116, -122.400307),
564   - new google.maps.LatLng(37.785282, -122.400539),
565   - new google.maps.LatLng(37.785346, -122.400692),
566   - new google.maps.LatLng(37.765769, -122.407201),
567   - new google.maps.LatLng(37.765790, -122.407414),
568   - new google.maps.LatLng(37.765802, -122.407755),
569   - new google.maps.LatLng(37.765791, -122.408219),
570   - new google.maps.LatLng(37.765763, -122.408759),
571   - new google.maps.LatLng(37.765726, -122.409348),
572   - new google.maps.LatLng(37.765716, -122.409882),
573   - new google.maps.LatLng(37.765708, -122.410202),
574   - new google.maps.LatLng(37.765705, -122.410253),
575   - new google.maps.LatLng(37.765707, -122.410369),
576   - new google.maps.LatLng(37.765692, -122.410720),
577   - new google.maps.LatLng(37.765699, -122.411215),
578   - new google.maps.LatLng(37.765687, -122.411789),
579   - new google.maps.LatLng(37.765666, -122.412373),
580   - new google.maps.LatLng(37.765598, -122.412883),
581   - new google.maps.LatLng(37.765543, -122.413039),
582   - new google.maps.LatLng(37.765532, -122.413125),
583   - new google.maps.LatLng(37.765500, -122.413553),
584   - new google.maps.LatLng(37.765448, -122.414053),
585   - new google.maps.LatLng(37.765388, -122.414645),
586   - new google.maps.LatLng(37.765323, -122.415250),
587   - new google.maps.LatLng(37.765303, -122.415847),
588   - new google.maps.LatLng(37.765251, -122.416439),
589   - new google.maps.LatLng(37.765204, -122.417020),
590   - new google.maps.LatLng(37.765172, -122.417556),
591   - new google.maps.LatLng(37.765164, -122.418075),
592   - new google.maps.LatLng(37.765153, -122.418618),
593   - new google.maps.LatLng(37.765136, -122.419112),
594   - new google.maps.LatLng(37.765129, -122.419378),
595   - new google.maps.LatLng(37.765119, -122.419481),
596   - new google.maps.LatLng(37.765100, -122.419852),
597   - new google.maps.LatLng(37.765083, -122.420349),
598   - new google.maps.LatLng(37.765045, -122.420930),
599   - new google.maps.LatLng(37.764992, -122.421481),
600   - new google.maps.LatLng(37.764980, -122.421695),
601   - new google.maps.LatLng(37.764993, -122.421843),
602   - new google.maps.LatLng(37.764986, -122.422255),
603   - new google.maps.LatLng(37.764975, -122.422823),
604   - new google.maps.LatLng(37.764939, -122.423411),
605   - new google.maps.LatLng(37.764902, -122.424014),
606   - new google.maps.LatLng(37.764853, -122.424576),
607   - new google.maps.LatLng(37.764826, -122.424922),
608   - new google.maps.LatLng(37.764796, -122.425375),
609   - new google.maps.LatLng(37.764782, -122.425869),
610   - new google.maps.LatLng(37.764768, -122.426089),
611   - new google.maps.LatLng(37.764766, -122.426117),
612   - new google.maps.LatLng(37.764723, -122.426276),
613   - new google.maps.LatLng(37.764681, -122.426649),
614   - new google.maps.LatLng(37.782012, -122.404200),
615   - new google.maps.LatLng(37.781574, -122.404911),
616   - new google.maps.LatLng(37.781055, -122.405597),
617   - new google.maps.LatLng(37.780479, -122.406341),
618   - new google.maps.LatLng(37.779996, -122.406939),
619   - new google.maps.LatLng(37.779459, -122.407613),
620   - new google.maps.LatLng(37.778953, -122.408228),
621   - new google.maps.LatLng(37.778409, -122.408839),
622   - new google.maps.LatLng(37.777842, -122.409501),
623   - new google.maps.LatLng(37.777334, -122.410181),
624   - new google.maps.LatLng(37.776809, -122.410836),
625   - new google.maps.LatLng(37.776240, -122.411514),
626   - new google.maps.LatLng(37.775725, -122.412145),
627   - new google.maps.LatLng(37.775190, -122.412805),
628   - new google.maps.LatLng(37.774672, -122.413464),
629   - new google.maps.LatLng(37.774084, -122.414186),
630   - new google.maps.LatLng(37.773533, -122.413636),
631   - new google.maps.LatLng(37.773021, -122.413009),
632   - new google.maps.LatLng(37.772501, -122.412371),
633   - new google.maps.LatLng(37.771964, -122.411681),
634   - new google.maps.LatLng(37.771479, -122.411078),
635   - new google.maps.LatLng(37.770992, -122.410477),
636   - new google.maps.LatLng(37.770467, -122.409801),
637   - new google.maps.LatLng(37.770090, -122.408904),
638   - new google.maps.LatLng(37.769657, -122.408103),
639   - new google.maps.LatLng(37.769132, -122.407276),
640   - new google.maps.LatLng(37.768564, -122.406469),
641   - new google.maps.LatLng(37.767980, -122.405745),
642   - new google.maps.LatLng(37.767380, -122.405299),
643   - new google.maps.LatLng(37.766604, -122.405297),
644   - new google.maps.LatLng(37.765838, -122.405200),
645   - new google.maps.LatLng(37.765139, -122.405139),
646   - new google.maps.LatLng(37.764457, -122.405094),
647   - new google.maps.LatLng(37.763716, -122.405142),
648   - new google.maps.LatLng(37.762932, -122.405398),
649   - new google.maps.LatLng(37.762126, -122.405813),
650   - new google.maps.LatLng(37.761344, -122.406215),
651   - new google.maps.LatLng(37.760556, -122.406495),
652   - new google.maps.LatLng(37.759732, -122.406484),
653   - new google.maps.LatLng(37.758910, -122.406228),
654   - new google.maps.LatLng(37.758182, -122.405695),
655   - new google.maps.LatLng(37.757676, -122.405118),
656   - new google.maps.LatLng(37.757039, -122.404346),
657   - new google.maps.LatLng(37.756335, -122.403719),
658   - new google.maps.LatLng(37.755503, -122.403406),
659   - new google.maps.LatLng(37.754665, -122.403242),
660   - new google.maps.LatLng(37.753837, -122.403172),
661   - new google.maps.LatLng(37.752986, -122.403112),
662   - new google.maps.LatLng(37.751266, -122.403355)
663   - ];
  293 + function toggleHeatmap(){
  294 + //add the heatmap to the thing
  295 + }
  296 +
  297 + function setGrade(grade){
  298 + var currGrade = document.getElementsByName("grade")[0].innerHTML = grade;
  299 + if(currGrade == "N/A"){
  300 + if(grade != "N/A"){
  301 + document.getElementsByName("grade")[0].innerHTML = grade;
  302 + }
  303 + } else {
  304 + if(currGrade > grade){
  305 + document.getElementsByName("grade")[0].innerHTML = grade;
  306 + }
  307 + }
  308 + }
  309 +
  310 + function clearGrade(){
  311 + document.getElementsByName("grade")[0].innerHTML = "";
  312 + }
  313 +
  314 + function setColor(score, circle){
  315 + if(score == 0){
  316 + //set color to blue
  317 + circle.setOptions({fillColor: '#3498db', strokeColor: '#3498db' });
  318 + } else if(0 < score && score < 20){
  319 + //set color to red
  320 + circle.setOptions({fillColor: '#e74c3c', strokeColor: '#e74c3c' });
  321 + } else if(20 <= score && score < 40){
  322 + //set orange
  323 + circle.setOptions({fillColor: '#e67e22', strokeColor: '#e67e22' });
  324 + } else if(40 <= score && score < 60){
  325 + //set yellow
  326 + circle.setOptions({fillColor: '#f1c40f', strokeColor: '#f1c40f' });
  327 + } else if(60 <= score && score < 80){
  328 + //light green
  329 + circle.setOptions({fillColor: '#2ecc71', strokeColor: '#2ecc71' });
  330 + } else {
  331 + //dark green
  332 + circle.setOptions({fillColor: '#27ae60', strokeColor: '#27ae60' });
  333 + }
  334 + }
  335 +
  336 + function removeCircles(){
  337 + for(var i = 0; i< circleArray.length; i++){
  338 + circleArray[i].setMap(null);
  339 + }
  340 + circleArray = [];
  341 + }
  342 +
  343 + function getCircle() {
  344 + return {
  345 + path: google.maps.SymbolPath.CIRCLE,
  346 + fillColor: 'red',
  347 + fillOpacity: .8,
  348 + scale: 10,
  349 + strokeColor: 'white',
  350 + strokeWeight: .5
  351 + };
664 352 }
665 353 </script>
666 354  
... ... @@ -672,24 +360,13 @@
672 360 <!-- Buttons at the bottom of the map -->
673 361 <div class="container">
674 362 <div class="row">
675   - <!-- Status -->
676   - <div class="col-xs-4">
677   - <!-- Title: Current Area -->
678   - <div>
679   - Current Area:
680   - </div>
681   - <!-- Icons + Status -->
682   - <div>
683   - 80% Safe
684   - </div>
685   - </div>
686 363 <!-- Emergency -->
687   - <div class="col-xs-4">
688   - <button type="button" class="btn btn-danger btn-block" onclick="emergency()">Emergency</button>
  364 + <div class="col-xs-6">
  365 + <button type="button" class="btn btn-danger btn-block text-center" onclick="emergency()">Emergency</button>
689 366 </div>
690 367 <!-- Open Map -->
691   - <div class="col-xs-4">
692   - <button type="button" class="btn btn-default btn-block" onclick="todo()">Open in Map</button>
  368 + <div class="col-xs-6">
  369 + <button type="button" class="btn btn-default btn-block text-center" onclick="openInMaps()">Open in Map</button>
693 370 </div>
694 371 </div>
695 372 </div>
... ... @@ -707,9 +384,30 @@
707 384 }
708 385 }
709 386  
  387 + function openInMaps(){
  388 +
  389 +
  390 +
  391 + if(window.mobilecheck() == true){
  392 + var str = 'comgooglemaps://?saddr='.concat((document.getElementById('origin').value).concat('&daddr=').concat(document.getElementById('dest').value).concat('&directionsmode').concat(localStorage.getItem('mode')));
  393 + window.location = str;
  394 + } else {
  395 + alert("This feature only works for mobile devices");
  396 + }
  397 +
  398 +
  399 + }
  400 +
710 401 function todo() {
711 402 alert("This button is brought to you by the wonderful Wizard of Oz.");
712 403 }
  404 +
  405 +
  406 + window.mobilecheck = function() {
  407 + var check = false;
  408 + (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
  409 + return check;
  410 + };
713 411 </script>
714 412  
715 413 </body>
views/pages/login.ejs View file @ 77ade48
... ... @@ -8,13 +8,13 @@
8 8  
9 9 <body>
10 10 <div class="container-fluid">
11   - <form action="/">
12   - <div class="form-group">
  11 + <form action="/">
  12 + <div class="form-group">
13 13 <input type="hidden" name="loggedin" value="true"/>
14   - <input type="currentLocation" class="form-control" id="origin" placeholder="Username" onchange="saveFrom()">
15   - <input type="currentLocation" class="form-control" id="origin" placeholder="Password" onchange="saveFrom()">
16   - </div>
17   - <button class="btn btn-primary">Log In</button>
  14 + <input type="currentLocation" class="form-control" id="origin" placeholder="Username" onchange="saveFrom()">
  15 + <input type="currentLocation" class="form-control" id="origin" placeholder="Password" onchange="saveFrom()">
  16 + </div>
  17 + <button class="btn btn-primary">Log In</button>
18 18 </form>
19 19 </div>
20 20