Commit ef269e4eef3e14f0ea772702e015a995e44293da

Authored by Paul Wallace
1 parent 20d5b2c910
Exists in master

Update to safe03

Showing 6 changed files with 1050 additions and 619 deletions Side-by-side Diff

public/scripts/grade.js View file @ ef269e4
  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 +}
public/scripts/routeboxer.js View file @ ef269e4
  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 +};
public/stylesheets/main.css View file @ ef269e4
  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 }
... ... @@ -38,4 +53,29 @@
38 53 .log-in-textfield{
39 54 width: 30%;
40 55 }
  56 +
  57 +#floating-panel {
  58 + position: absolute;
  59 + top: 10px;
  60 + left: 25%;
  61 + z-index: 5;
  62 + background-color: #fff;
  63 + padding: 5px;
  64 + border: 1px solid #999;
  65 + text-align: center;
  66 + font-family: 'Roboto','sans-serif';
  67 + line-height: 30px;
  68 + padding-left: 10px;
  69 +}
  70 +#floating-panel {
  71 + background-color: #fff;
  72 + border: 1px solid #999;
  73 + left: 25%;
  74 + padding: 5px;
  75 + position: absolute;
  76 + top: 10px;
  77 + z-index: 5;
  78 +}
  79 +/**
  80 +*/
views/pages/index.ejs View file @ ef269e4
... ... @@ -13,6 +13,9 @@
13 13 <p>The best way to find the safest routes.</p>
14 14 </div>
15 15  
  16 +
  17 +
  18 +
16 19 <% if(!locals.loggedin || (locals.loggedin != "true")){ %>
17 20  
18 21 <%console.log(locals.loggedin)%>
... ... @@ -22,6 +25,7 @@
22 25 <!-- <input type="submit" value="Submit"> -->
23 26 </form>
24 27 <% } %>
  28 +
25 29  
26 30  
27 31  
28 32  
... ... @@ -30,13 +34,13 @@
30 34 <form action="/landing">
31 35 <!-- First text field and label above the text field for current location -->
32 36 <div class="form-group text-center">
33   - <label for="currentLocation">From</label>
34   - <input type="currentLocation" class="form-control" id="origin" placeholder="Enter Current Location" onchange="saveFrom()">
  37 + <label for="origin">From</label>
  38 + <input type="text" class="form-control" id="origin" placeholder="Enter Current Location" onclick="initFrom()">
35 39 </div>
36 40 <!-- Second text field and label above the text field for destination -->
37 41 <div class="form-group text-center">
38   - <label for="destination">To</label>
39   - <input type="destination" class="form-control" id="dest" placeholder="Enter Destination" onchange="saveTo()">
  42 + <label for="dest">To</label>
  43 + <input type="text" class="form-control" id="dest" placeholder="Enter Destination" onclick="initTo()">
40 44 </div>
41 45 <!-- First travel option button, walking
42 46 This contains a row that holds buttons of type button. Buttons are highlighted upon selection.
43 47  
... ... @@ -65,9 +69,66 @@
65 69  
66 70 <!-- Google Map functionality. -->
67 71 <script>
  72 + function initFrom() {
  73 + var temp = document.getElementById("origin");
  74 +
  75 + var autoOrigin = new google.maps.places.Autocomplete(temp);
  76 +
  77 + google.maps.event.addListener(autoOrigin, 'place_changed', function () {
  78 + var place = autoOrigin.getPlace();
  79 + // Testing.
  80 + console.log(place);
  81 + //localStorage.setItem("from", place.formatted_address);
  82 + localStorage.setItem("fromIn", temp.value);
  83 + localStorage.setItem("originLat", place.geometry.location.lat());
  84 + localStorage.setItem("originLng", place.geometry.location.lng());
  85 + });
  86 + }
  87 +
  88 + function initTo() {
  89 + var temp = document.getElementById("dest");
  90 +
  91 + var autoDest = new google.maps.places.Autocomplete(temp);
  92 +
  93 + google.maps.event.addListener(autoDest, 'place_changed', function () {
  94 + var place = autoDest.getPlace();
  95 + // Testing.
  96 + console.log(place);
  97 + //localStorage.setItem("to", place.formatted_address);
  98 + localStorage.setItem("toIn", temp.value);
  99 + localStorage.setItem("destLat", place.geometry.location.lat());
  100 + localStorage.setItem("destLng", place.geometry.location.lng());
  101 + });
  102 + }
  103 +
  104 + if (navigator.geolocation) {
  105 + navigator.geolocation.getCurrentPosition(function(pos) {
  106 + var geocoder = new google.maps.Geocoder;
  107 + var point = new google.maps.LatLng(
  108 + pos.coords.latitude, pos.coords.longitude);
  109 +
  110 + localStorage.setItem("originLat", pos.coords.latitude);
  111 + localStorage.setItem("originLng", pos.coords.longitude);
  112 +
  113 + geocoder.geocode({'latLng': point}, function (locations, status) {
  114 + if (status == google.maps.GeocoderStatus.OK) {
  115 + for (var location of locations) {
  116 + if ($.inArray("street_address", location.types) != -1) {
  117 + console.log('Your location is: ' + location.formatted_address);
  118 + document.getElementById("origin").value = location.formatted_address;
  119 + saveFrom();
  120 + break;
  121 + }
  122 + };
  123 + }
  124 + });
  125 + });
  126 + }
  127 +
68 128 function saveFrom() {
69 129 var fromInput = document.getElementById("origin").value;
70   - localStorage.setItem("from", fromInput);
  130 + localStorage.setItem("from", fromInput);
  131 + localStorage.setItem("fromIn", fromInput);
71 132 }
72 133 function saveTo() {
73 134 var toInput = document.getElementById("dest").value;
... ... @@ -86,6 +147,10 @@
86 147 function login(){
87 148  
88 149 }
  150 + </script>
  151 +
  152 + <script async defer
  153 + src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDAXDHeZXEv4H4ZnThVDxpyAuxVpzOcj_U&libraries=places">
89 154 </script>
90 155  
91 156 </body>
views/pages/landing.ejs View file @ ef269e4
Diff suppressed. Click to show
... ... @@ -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  
12 21  
... ... @@ -14,11 +23,11 @@
14 23  
15 24 <!-- My LOCATION -->
16 25 <div>
17   - <input type="text" class="form-control text-field" id="from" name="fromText" placeholder="MY LOCATION">
  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="to" name="toText" placeholder="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna...">
  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. -->
25 34  
26 35  
27 36  
28 37  
29 38  
30 39  
31 40  
32 41  
33 42  
34 43  
35 44  
36 45  
37 46  
38 47  
39 48  
40 49  
41 50  
42 51  
43 52  
44 53  
45 54  
46 55  
47 56  
48 57  
49 58  
50 59  
51 60  
52 61  
53 62  
54 63  
55 64  
56 65  
... ... @@ -28,655 +37,379 @@
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 + -->
54 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>
  90 +
55 91 <!-- Javascript for the map. -->
56 92 <script>
57   -
58   - var map, heatmap;
59   - function initMap() {
  93 +
  94 + var map, heatmap;
60 95 // Used by the Google Maps Direction API.
61   - var directionsService = new google.maps.DirectionsService;
62   - var directionsDisplay = new google.maps.DirectionsRenderer;
63   - // Setting default map location.
  96 + var directionsService;
  97 + var directionsDisplay;
  98 + //array to hold the circle added to map
  99 + var circleArray;
64 100  
65   - map = new google.maps.Map(document.getElementById('map'), {
66   - zoom: 13,
67   - center: {lat: 37.775, lng: -122.434}
68   - });
  101 + function initMap() {
  102 + circleArray = [];
  103 + directionsService = new google.maps.DirectionsService;
  104 + directionsDisplay = new google.maps.DirectionsRenderer;
  105 + // Setting default map location i.e. Geisel Library.
  106 + map = new google.maps.Map(document.getElementById('map'), {
  107 + zoom: 13,
  108 + center: {lat: 37.775, lng: -122.434}
  109 + });
  110 + directionsDisplay.setMap(map);
  111 + directionsDisplay.setPanel(document.getElementById('right-panel'));
  112 + // Keeping the map center when the browser is resized.
  113 + function resizing() {
  114 + var center = map.getCenter();
  115 + google.maps.event.trigger(map, "resize");
  116 + map.setCenter(center);
  117 + }
69 118  
70   - heatmap = new google.maps.visualization.HeatmapLayer({
71   - data: getPoints(),
72   - map: map
73   - });
74   - directionsDisplay.setMap(map);
  119 + google.maps.event.addDomListener(window, "resize", resizing);
  120 + // Show route.
  121 + displayRoute(directionsService, directionsDisplay);
  122 + }
75 123  
76   - // Keeping the map center when the browser is resized.
77   - function resizing() {
78   - var center = map.getCenter();
79   - google.maps.event.trigger(map, "resize");
80   - map.setCenter(center);
  124 + function displayRoute(directionsService, directionsDisplay) {
  125 + // Textfields that show from and to.
  126 + document.getElementById("origin").value = localStorage.getItem("fromIn");
  127 + document.getElementById("dest").value = localStorage.getItem("toIn");
  128 +
  129 + // Default mode to driving if none was chosen.
  130 + var mode = localStorage.getItem("mode");
  131 +
  132 + if (mode != "WALKING" && mode != "DRIVING" && mode != "TRANSIT") {
  133 + mode = "DRIVING";
  134 + }
  135 +
  136 + var originPoint = new google.maps.LatLng(localStorage.getItem("originLat"), localStorage.getItem("originLng"));
  137 + var destPoint = new google.maps.LatLng(localStorage.getItem("destLat"), localStorage.getItem("destLng"));
  138 +
  139 + var request = {
  140 + origin: originPoint,
  141 + destination: destPoint,
  142 + travelMode: mode
  143 + };
  144 +
  145 + directionsService.route(request, function(response, status) {
  146 + if (status === "OK") {
  147 + console.log(response);
  148 + directionsDisplay.setDirections(response);
  149 + document.getElementsByName("time")[0].innerHTML = response.routes[0].legs[0].duration.text
  150 + //Set up the markers to show danger in different areas
  151 + getPoints(response.routes[0]);
  152 + }
  153 + else {
  154 + window.alert("Directions request failed due to " + status);
  155 + }
  156 + });
81 157 }
82 158  
83   - google.maps.event.addDomListener(window, "resize", resizing);
  159 + function initFrom() {
  160 + var temp = document.getElementById("origin");
  161 + var autoOrigin = new google.maps.places.Autocomplete(temp);
  162 + google.maps.event.addListener(autoOrigin, 'place_changed', function () {
  163 + var place = autoOrigin.getPlace();
  164 + // Testing.
  165 + console.log(place);
  166 + localStorage.setItem("fromIn", temp.value);
  167 + localStorage.setItem("originLat", place.geometry.location.lat());
  168 + localStorage.setItem("originLng", place.geometry.location.lng());
  169 + //Prevent memory leak, remove circle from last route
  170 + removeCircles();
  171 + clearGrade();
  172 + displayRoute(directionsService, directionsDisplay);
  173 + });
  174 + }
84 175  
85   - // // Showing route.
86   - displayRoute(directionsService, directionsDisplay);
87   - }
  176 + function initTo() {
  177 + var temp = document.getElementById("dest");
  178 + var autoDest = new google.maps.places.Autocomplete(temp);
  179 + google.maps.event.addListener(autoDest, 'place_changed', function () {
  180 + var place = autoDest.getPlace();
  181 + // Testing.
  182 + console.log(place);
  183 + localStorage.setItem("toIn", temp.value);
  184 + localStorage.setItem("destLat", place.geometry.location.lat());
  185 + localStorage.setItem("destLng", place.geometry.location.lng());
  186 + //Prevent memory leak, remove circle from last route
  187 + removeCircles();
  188 + clearGrade();
  189 + displayRoute(directionsService, directionsDisplay);
  190 + });
  191 + }
88 192  
89   - function displayRoute(directionsService, directionsDisplay) {
90   - // Textfields that show from and to.
91   - document.getElementsByName("fromText")[0].placeholder=localStorage.getItem("from");
92   - document.getElementsByName("toText")[0].placeholder=localStorage.getItem("to");
  193 + function getPoints(route) {
  194 + //set up HTTP client to make GET request to API
  195 + var HttpClient = function() {
  196 + this.get = function(aUrl, aCallback) {
  197 + var anHttpRequest = new XMLHttpRequest();
  198 + anHttpRequest.onreadystatechange = function() {
  199 + if (anHttpRequest.readyState == 4 && anHttpRequest.status == 200)
  200 + aCallback(anHttpRequest.responseText);
  201 + }
  202 + //gj4g6D3zxKmshys7yyf9KNbuywOxp1ueazFjsn6n2fK4sccIGA
  203 + anHttpRequest.open( "GET", aUrl, true );
  204 + anHttpRequest.setRequestHeader("X-Mashape-Key", "W4MVkGfi3ZmshbMN0LqORTZunzeTp1rEG5Pjsn9og1t7bRnQxG");
  205 + //W4MVkGfi3ZmshbMN0LqORTZunzeTp1rEG5Pjsn9og1t7bRnQxG
  206 + anHttpRequest.setRequestHeader("Accept", "application/json")
  207 + anHttpRequest.send( null );
  208 + }
  209 + }
93 210  
94   - var request = {
95   - origin: localStorage.getItem("from"),
96   - destination: localStorage.getItem("to"),
97   - travelMode: localStorage.getItem("mode")
98   - };
  211 + //Loop through the steps of the route to get the center point
  212 + var bounds = new google.maps.LatLngBounds();
  213 + var legs = route.legs;
  214 + for (i=0;i<legs.length;i++) {
  215 + var steps = legs[i].steps;
  216 + for (j=0;j<steps.length;j++) {
  217 + var nextSegment = steps[j].path;
  218 + for (k=0;k<nextSegment.length;k++) {
  219 + // polyline.getPath().push(nextSegment[k]);
  220 + bounds.extend(nextSegment[k]);
  221 + }
  222 + }
  223 + }
99 224  
100   - directionsService.route(request, function(response, status) {
101   - if (status === "OK") {
102   - directionsDisplay.setDirections(response);
103   - }
104   - else {
105   - window.alert("Directions request failed due to " + status);
  225 + //create instance of HTTP client
  226 + aClient = new HttpClient();
  227 + /*Also very possible to use RouteBoxer as a better solution*/
  228 + // for (var i = route.legs[0].steps.length - 1; i >= 0; i--) {
  229 +
  230 + for (var i = 0; i < 3; i++){
  231 + var long = route.legs[0].steps[i].end_point.lng();
  232 + var lat = route.legs[0].steps[i].end_point.lat();
  233 + if(i == 2){
  234 + long = route.legs[0].steps[route.legs[0].steps.length - 1].end_point.lng();
  235 + lat = route.legs[0].steps[route.legs[0].steps.length - 1].end_point.lat();
  236 + }
  237 +
  238 + //If middle point then get the center point from bounds
  239 + else if(i == 1){
  240 + long = bounds.getCenter().lng();
  241 + lat = bounds.getCenter().lat();
  242 + }
  243 + //Base string concatenated with GET request options
  244 + var str = "https://crimescore.p.mashape.com/crimescore?f=json&id=174&lat=".concat(lat).concat("&lon=").concat(long);
  245 + //Make the HTTP request
  246 + aClient.get(str, function(result) {
  247 + //check what the score is
  248 + var object = JSON.parse(result);
  249 + var score = parseInt(object.score);
  250 + var grade = object.grade;
  251 + console.log(result);
  252 +
  253 +
  254 + //Colors:
  255 + //Yellow: #f1c40f
  256 + //Red: #e74c3c
  257 + //Light Green: #2ecc71
  258 + //Dark Green: #27ae60
  259 + //Blue: #3498db
  260 +
  261 + //create the options object
  262 + var circle = new google.maps.Circle({
  263 + strokeColor: '#3498db',
  264 + strokeOpacity: 0.8,
  265 + strokeWeight: 2,
  266 + fillColor: '#3498db',
  267 + fillOpacity: 0.35,
  268 + map: map,
  269 + center: {lat: parseFloat(object.latitude), lng: parseFloat(object.longitude)},
  270 + radius: 804,
  271 + draggable: true
  272 + })
  273 +
  274 + setColor(score, circle);
  275 + setGrade(object.grade);
  276 + circleArray.push(circle);
  277 + circle.addListener('dragend', function(event){
  278 + var newLat = event.latLng.lat();
  279 + var newLong = event.latLng.lng();
  280 +
  281 + var newStr = "https://crimescore.p.mashape.com/crimescore?f=json&id=174&lat=".concat(newLat).concat("&lon=").concat(newLong)
  282 + aClient.get(newStr, function(result){
  283 + //change the color
  284 + var newObject = JSON.parse(result);
  285 + var newScore = parseInt(newObject.score);
  286 + setColor(newScore, circle);
  287 + // setGrade(object.grade);
  288 + });
  289 + });
  290 + });
106 291 }
107   - });
108   - }
  292 + }
109 293  
110   - function getPoints() {
111   - //read json here from passed in parameters
112   - //loop here that loops through the passed in json values for points along the route
113   - //return the list of objects created from those longitudes and latitudes
  294 + function toggleHeatmap(){
  295 + //add the heatmap to the thing
  296 + }
114 297  
115   - //THIS IS THE CODE TO READ JSON BUT IT'S NOT VIABLE TO CREATE ALL THESE POINTS MANUALLY IN A JSON FILE.
116   - //So instead we copy pasted a sample set of points to demonstrate the functionality
  298 + function setGrade(grade){
  299 + var currGrade = document.getElementsByName("grade")[0].innerHTML = grade;
  300 + if(currGrade == "N/A"){
  301 + if(grade != "N/A"){
  302 + document.getElementsByName("grade")[0].innerHTML = grade;
  303 + }
  304 + } else {
  305 + if(currGrade > grade){
  306 + document.getElementsByName("grade")[0].innerHTML = grade;
  307 + }
  308 + }
  309 + }
117 310  
118   - /*var toReturn = [];
119   - for (i = 0; i < [insert ejs symbols here]spots.length[insert ejs symbols here]; i++) {
120   - 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));
  311 + function clearGrade(){
  312 + document.getElementsByName("grade")[0].innerHTML = "";
121 313 }
122 314  
123   - return toReturn;*/
  315 + function setColor(score, circle){
  316 + if(score == 0){
  317 + //set color to blue
  318 + circle.setOptions({fillColor: '#3498db', strokeColor: '#3498db' });
  319 + } else if(0 < score && score < 20){
  320 + //set color to red
  321 + circle.setOptions({fillColor: '#e74c3c', strokeColor: '#e74c3c' });
  322 + } else if(20 <= score && score < 40){
  323 + //set orange
  324 + circle.setOptions({fillColor: '#e67e22', strokeColor: '#e67e22' });
  325 + } else if(40 <= score && score < 60){
  326 + //set yellow
  327 + circle.setOptions({fillColor: '#f1c40f', strokeColor: '#f1c40f' });
  328 + } else if(60 <= score && score < 80){
  329 + //light green
  330 + circle.setOptions({fillColor: '#2ecc71', strokeColor: '#2ecc71' });
  331 + } else {
  332 + //dark green
  333 + circle.setOptions({fillColor: '#27ae60', strokeColor: '#27ae60' });
  334 + }
  335 + }
124 336  
  337 + function removeCircles(){
  338 + for(var i = 0; i< circleArray.length; i++){
  339 + circleArray[i].setMap(null);
  340 + }
  341 + circleArray = [];
  342 + }
125 343  
126   - return [
127   - new google.maps.LatLng(37.782551, -122.445368),
128   - new google.maps.LatLng(37.782745, -122.444586),
129   - new google.maps.LatLng(37.782842, -122.443688),
130   - new google.maps.LatLng(37.782919, -122.442815),
131   - new google.maps.LatLng(37.782992, -122.442112),
132   - new google.maps.LatLng(37.783100, -122.441461),
133   - new google.maps.LatLng(37.783206, -122.440829),
134   - new google.maps.LatLng(37.783273, -122.440324),
135   - new google.maps.LatLng(37.783316, -122.440023),
136   - new google.maps.LatLng(37.783357, -122.439794),
137   - new google.maps.LatLng(37.783371, -122.439687),
138   - new google.maps.LatLng(37.783368, -122.439666),
139   - new google.maps.LatLng(37.783383, -122.439594),
140   - new google.maps.LatLng(37.783508, -122.439525),
141   - new google.maps.LatLng(37.783842, -122.439591),
142   - new google.maps.LatLng(37.784147, -122.439668),
143   - new google.maps.LatLng(37.784206, -122.439686),
144   - new google.maps.LatLng(37.784386, -122.439790),
145   - new google.maps.LatLng(37.784701, -122.439902),
146   - new google.maps.LatLng(37.784965, -122.439938),
147   - new google.maps.LatLng(37.785010, -122.439947),
148   - new google.maps.LatLng(37.785360, -122.439952),
149   - new google.maps.LatLng(37.785715, -122.440030),
150   - new google.maps.LatLng(37.786117, -122.440119),
151   - new google.maps.LatLng(37.786564, -122.440209),
152   - new google.maps.LatLng(37.786905, -122.440270),
153   - new google.maps.LatLng(37.786956, -122.440279),
154   - new google.maps.LatLng(37.800224, -122.433520),
155   - new google.maps.LatLng(37.800155, -122.434101),
156   - new google.maps.LatLng(37.800160, -122.434430),
157   - new google.maps.LatLng(37.800378, -122.434527),
158   - new google.maps.LatLng(37.800738, -122.434598),
159   - new google.maps.LatLng(37.800938, -122.434650),
160   - new google.maps.LatLng(37.801024, -122.434889),
161   - new google.maps.LatLng(37.800955, -122.435392),
162   - new google.maps.LatLng(37.800886, -122.435959),
163   - new google.maps.LatLng(37.800811, -122.436275),
164   - new google.maps.LatLng(37.800788, -122.436299),
165   - new google.maps.LatLng(37.800719, -122.436302),
166   - new google.maps.LatLng(37.800702, -122.436298),
167   - new google.maps.LatLng(37.800661, -122.436273),
168   - new google.maps.LatLng(37.800395, -122.436172),
169   - new google.maps.LatLng(37.800228, -122.436116),
170   - new google.maps.LatLng(37.800169, -122.436130),
171   - new google.maps.LatLng(37.800066, -122.436167),
172   - new google.maps.LatLng(37.784345, -122.422922),
173   - new google.maps.LatLng(37.784389, -122.422926),
174   - new google.maps.LatLng(37.784437, -122.422924),
175   - new google.maps.LatLng(37.784746, -122.422818),
176   - new google.maps.LatLng(37.785436, -122.422959),
177   - new google.maps.LatLng(37.786120, -122.423112),
178   - new google.maps.LatLng(37.786433, -122.423029),
179   - new google.maps.LatLng(37.786631, -122.421213),
180   - new google.maps.LatLng(37.786660, -122.421033),
181   - new google.maps.LatLng(37.786801, -122.420141),
182   - new google.maps.LatLng(37.786823, -122.420034),
183   - new google.maps.LatLng(37.786831, -122.419916),
184   - new google.maps.LatLng(37.787034, -122.418208),
185   - new google.maps.LatLng(37.787056, -122.418034),
186   - new google.maps.LatLng(37.787169, -122.417145),
187   - new google.maps.LatLng(37.787217, -122.416715),
188   - new google.maps.LatLng(37.786144, -122.416403),
189   - new google.maps.LatLng(37.785292, -122.416257),
190   - new google.maps.LatLng(37.780666, -122.390374),
191   - new google.maps.LatLng(37.780501, -122.391281),
192   - new google.maps.LatLng(37.780148, -122.392052),
193   - new google.maps.LatLng(37.780173, -122.391148),
194   - new google.maps.LatLng(37.780693, -122.390592),
195   - new google.maps.LatLng(37.781261, -122.391142),
196   - new google.maps.LatLng(37.781808, -122.391730),
197   - new google.maps.LatLng(37.782340, -122.392341),
198   - new google.maps.LatLng(37.782812, -122.393022),
199   - new google.maps.LatLng(37.783300, -122.393672),
200   - new google.maps.LatLng(37.783809, -122.394275),
201   - new google.maps.LatLng(37.784246, -122.394979),
202   - new google.maps.LatLng(37.784791, -122.395958),
203   - new google.maps.LatLng(37.785675, -122.396746),
204   - new google.maps.LatLng(37.786262, -122.395780),
205   - new google.maps.LatLng(37.786776, -122.395093),
206   - new google.maps.LatLng(37.787282, -122.394426),
207   - new google.maps.LatLng(37.787783, -122.393767),
208   - new google.maps.LatLng(37.788343, -122.393184),
209   - new google.maps.LatLng(37.788895, -122.392506),
210   - new google.maps.LatLng(37.789371, -122.391701),
211   - new google.maps.LatLng(37.789722, -122.390952),
212   - new google.maps.LatLng(37.790315, -122.390305),
213   - new google.maps.LatLng(37.790738, -122.389616),
214   - new google.maps.LatLng(37.779448, -122.438702),
215   - new google.maps.LatLng(37.779023, -122.438585),
216   - new google.maps.LatLng(37.778542, -122.438492),
217   - new google.maps.LatLng(37.778100, -122.438411),
218   - new google.maps.LatLng(37.777986, -122.438376),
219   - new google.maps.LatLng(37.777680, -122.438313),
220   - new google.maps.LatLng(37.777316, -122.438273),
221   - new google.maps.LatLng(37.777135, -122.438254),
222   - new google.maps.LatLng(37.776987, -122.438303),
223   - new google.maps.LatLng(37.776946, -122.438404),
224   - new google.maps.LatLng(37.776944, -122.438467),
225   - new google.maps.LatLng(37.776892, -122.438459),
226   - new google.maps.LatLng(37.776842, -122.438442),
227   - new google.maps.LatLng(37.776822, -122.438391),
228   - new google.maps.LatLng(37.776814, -122.438412),
229   - new google.maps.LatLng(37.776787, -122.438628),
230   - new google.maps.LatLng(37.776729, -122.438650),
231   - new google.maps.LatLng(37.776759, -122.438677),
232   - new google.maps.LatLng(37.776772, -122.438498),
233   - new google.maps.LatLng(37.776787, -122.438389),
234   - new google.maps.LatLng(37.776848, -122.438283),
235   - new google.maps.LatLng(37.776870, -122.438239),
236   - new google.maps.LatLng(37.777015, -122.438198),
237   - new google.maps.LatLng(37.777333, -122.438256),
238   - new google.maps.LatLng(37.777595, -122.438308),
239   - new google.maps.LatLng(37.777797, -122.438344),
240   - new google.maps.LatLng(37.778160, -122.438442),
241   - new google.maps.LatLng(37.778414, -122.438508),
242   - new google.maps.LatLng(37.778445, -122.438516),
243   - new google.maps.LatLng(37.778503, -122.438529),
244   - new google.maps.LatLng(37.778607, -122.438549),
245   - new google.maps.LatLng(37.778670, -122.438644),
246   - new google.maps.LatLng(37.778847, -122.438706),
247   - new google.maps.LatLng(37.779240, -122.438744),
248   - new google.maps.LatLng(37.779738, -122.438822),
249   - new google.maps.LatLng(37.780201, -122.438882),
250   - new google.maps.LatLng(37.780400, -122.438905),
251   - new google.maps.LatLng(37.780501, -122.438921),
252   - new google.maps.LatLng(37.780892, -122.438986),
253   - new google.maps.LatLng(37.781446, -122.439087),
254   - new google.maps.LatLng(37.781985, -122.439199),
255   - new google.maps.LatLng(37.782239, -122.439249),
256   - new google.maps.LatLng(37.782286, -122.439266),
257   - new google.maps.LatLng(37.797847, -122.429388),
258   - new google.maps.LatLng(37.797874, -122.429180),
259   - new google.maps.LatLng(37.797885, -122.429069),
260   - new google.maps.LatLng(37.797887, -122.429050),
261   - new google.maps.LatLng(37.797933, -122.428954),
262   - new google.maps.LatLng(37.798242, -122.428990),
263   - new google.maps.LatLng(37.798617, -122.429075),
264   - new google.maps.LatLng(37.798719, -122.429092),
265   - new google.maps.LatLng(37.798944, -122.429145),
266   - new google.maps.LatLng(37.799320, -122.429251),
267   - new google.maps.LatLng(37.799590, -122.429309),
268   - new google.maps.LatLng(37.799677, -122.429324),
269   - new google.maps.LatLng(37.799966, -122.429360),
270   - new google.maps.LatLng(37.800288, -122.429430),
271   - new google.maps.LatLng(37.800443, -122.429461),
272   - new google.maps.LatLng(37.800465, -122.429474),
273   - new google.maps.LatLng(37.800644, -122.429540),
274   - new google.maps.LatLng(37.800948, -122.429620),
275   - new google.maps.LatLng(37.801242, -122.429685),
276   - new google.maps.LatLng(37.801375, -122.429702),
277   - new google.maps.LatLng(37.801400, -122.429703),
278   - new google.maps.LatLng(37.801453, -122.429707),
279   - new google.maps.LatLng(37.801473, -122.429709),
280   - new google.maps.LatLng(37.801532, -122.429707),
281   - new google.maps.LatLng(37.801852, -122.429729),
282   - new google.maps.LatLng(37.802173, -122.429789),
283   - new google.maps.LatLng(37.802459, -122.429847),
284   - new google.maps.LatLng(37.802554, -122.429825),
285   - new google.maps.LatLng(37.802647, -122.429549),
286   - new google.maps.LatLng(37.802693, -122.429179),
287   - new google.maps.LatLng(37.802729, -122.428751),
288   - new google.maps.LatLng(37.766104, -122.409291),
289   - new google.maps.LatLng(37.766103, -122.409268),
290   - new google.maps.LatLng(37.766138, -122.409229),
291   - new google.maps.LatLng(37.766183, -122.409231),
292   - new google.maps.LatLng(37.766153, -122.409276),
293   - new google.maps.LatLng(37.766005, -122.409365),
294   - new google.maps.LatLng(37.765897, -122.409570),
295   - new google.maps.LatLng(37.765767, -122.409739),
296   - new google.maps.LatLng(37.765693, -122.410389),
297   - new google.maps.LatLng(37.765615, -122.411201),
298   - new google.maps.LatLng(37.765533, -122.412121),
299   - new google.maps.LatLng(37.765467, -122.412939),
300   - new google.maps.LatLng(37.765444, -122.414821),
301   - new google.maps.LatLng(37.765444, -122.414964),
302   - new google.maps.LatLng(37.765318, -122.415424),
303   - new google.maps.LatLng(37.763961, -122.415296),
304   - new google.maps.LatLng(37.763115, -122.415196),
305   - new google.maps.LatLng(37.762967, -122.415183),
306   - new google.maps.LatLng(37.762278, -122.415127),
307   - new google.maps.LatLng(37.761675, -122.415055),
308   - new google.maps.LatLng(37.760932, -122.414988),
309   - new google.maps.LatLng(37.759337, -122.414862),
310   - new google.maps.LatLng(37.773187, -122.421922),
311   - new google.maps.LatLng(37.773043, -122.422118),
312   - new google.maps.LatLng(37.773007, -122.422165),
313   - new google.maps.LatLng(37.772979, -122.422219),
314   - new google.maps.LatLng(37.772865, -122.422394),
315   - new google.maps.LatLng(37.772779, -122.422503),
316   - new google.maps.LatLng(37.772676, -122.422701),
317   - new google.maps.LatLng(37.772606, -122.422806),
318   - new google.maps.LatLng(37.772566, -122.422840),
319   - new google.maps.LatLng(37.772508, -122.422852),
320   - new google.maps.LatLng(37.772387, -122.423011),
321   - new google.maps.LatLng(37.772099, -122.423328),
322   - new google.maps.LatLng(37.771704, -122.423783),
323   - new google.maps.LatLng(37.771481, -122.424081),
324   - new google.maps.LatLng(37.771400, -122.424179),
325   - new google.maps.LatLng(37.771352, -122.424220),
326   - new google.maps.LatLng(37.771248, -122.424327),
327   - new google.maps.LatLng(37.770904, -122.424781),
328   - new google.maps.LatLng(37.770520, -122.425283),
329   - new google.maps.LatLng(37.770337, -122.425553),
330   - new google.maps.LatLng(37.770128, -122.425832),
331   - new google.maps.LatLng(37.769756, -122.426331),
332   - new google.maps.LatLng(37.769300, -122.426902),
333   - new google.maps.LatLng(37.769132, -122.427065),
334   - new google.maps.LatLng(37.769092, -122.427103),
335   - new google.maps.LatLng(37.768979, -122.427172),
336   - new google.maps.LatLng(37.768595, -122.427634),
337   - new google.maps.LatLng(37.768372, -122.427913),
338   - new google.maps.LatLng(37.768337, -122.427961),
339   - new google.maps.LatLng(37.768244, -122.428138),
340   - new google.maps.LatLng(37.767942, -122.428581),
341   - new google.maps.LatLng(37.767482, -122.429094),
342   - new google.maps.LatLng(37.767031, -122.429606),
343   - new google.maps.LatLng(37.766732, -122.429986),
344   - new google.maps.LatLng(37.766680, -122.430058),
345   - new google.maps.LatLng(37.766633, -122.430109),
346   - new google.maps.LatLng(37.766580, -122.430211),
347   - new google.maps.LatLng(37.766367, -122.430594),
348   - new google.maps.LatLng(37.765910, -122.431137),
349   - new google.maps.LatLng(37.765353, -122.431806),
350   - new google.maps.LatLng(37.764962, -122.432298),
351   - new google.maps.LatLng(37.764868, -122.432486),
352   - new google.maps.LatLng(37.764518, -122.432913),
353   - new google.maps.LatLng(37.763435, -122.434173),
354   - new google.maps.LatLng(37.762847, -122.434953),
355   - new google.maps.LatLng(37.762291, -122.435935),
356   - new google.maps.LatLng(37.762224, -122.436074),
357   - new google.maps.LatLng(37.761957, -122.436892),
358   - new google.maps.LatLng(37.761652, -122.438886),
359   - new google.maps.LatLng(37.761284, -122.439955),
360   - new google.maps.LatLng(37.761210, -122.440068),
361   - new google.maps.LatLng(37.761064, -122.440720),
362   - new google.maps.LatLng(37.761040, -122.441411),
363   - new google.maps.LatLng(37.761048, -122.442324),
364   - new google.maps.LatLng(37.760851, -122.443118),
365   - new google.maps.LatLng(37.759977, -122.444591),
366   - new google.maps.LatLng(37.759913, -122.444698),
367   - new google.maps.LatLng(37.759623, -122.445065),
368   - new google.maps.LatLng(37.758902, -122.445158),
369   - new google.maps.LatLng(37.758428, -122.444570),
370   - new google.maps.LatLng(37.757687, -122.443340),
371   - new google.maps.LatLng(37.757583, -122.443240),
372   - new google.maps.LatLng(37.757019, -122.442787),
373   - new google.maps.LatLng(37.756603, -122.442322),
374   - new google.maps.LatLng(37.756380, -122.441602),
375   - new google.maps.LatLng(37.755790, -122.441382),
376   - new google.maps.LatLng(37.754493, -122.442133),
377   - new google.maps.LatLng(37.754361, -122.442206),
378   - new google.maps.LatLng(37.753719, -122.442650),
379   - new google.maps.LatLng(37.753096, -122.442915),
380   - new google.maps.LatLng(37.751617, -122.443211),
381   - new google.maps.LatLng(37.751496, -122.443246),
382   - new google.maps.LatLng(37.750733, -122.443428),
383   - new google.maps.LatLng(37.750126, -122.443536),
384   - new google.maps.LatLng(37.750103, -122.443784),
385   - new google.maps.LatLng(37.750390, -122.444010),
386   - new google.maps.LatLng(37.750448, -122.444013),
387   - new google.maps.LatLng(37.750536, -122.444040),
388   - new google.maps.LatLng(37.750493, -122.444141),
389   - new google.maps.LatLng(37.790859, -122.402808),
390   - new google.maps.LatLng(37.790864, -122.402768),
391   - new google.maps.LatLng(37.790995, -122.402539),
392   - new google.maps.LatLng(37.791148, -122.402172),
393   - new google.maps.LatLng(37.791385, -122.401312),
394   - new google.maps.LatLng(37.791405, -122.400776),
395   - new google.maps.LatLng(37.791288, -122.400528),
396   - new google.maps.LatLng(37.791113, -122.400441),
397   - new google.maps.LatLng(37.791027, -122.400395),
398   - new google.maps.LatLng(37.791094, -122.400311),
399   - new google.maps.LatLng(37.791211, -122.400183),
400   - new google.maps.LatLng(37.791060, -122.399334),
401   - new google.maps.LatLng(37.790538, -122.398718),
402   - new google.maps.LatLng(37.790095, -122.398086),
403   - new google.maps.LatLng(37.789644, -122.397360),
404   - new google.maps.LatLng(37.789254, -122.396844),
405   - new google.maps.LatLng(37.788855, -122.396397),
406   - new google.maps.LatLng(37.788483, -122.395963),
407   - new google.maps.LatLng(37.788015, -122.395365),
408   - new google.maps.LatLng(37.787558, -122.394735),
409   - new google.maps.LatLng(37.787472, -122.394323),
410   - new google.maps.LatLng(37.787630, -122.394025),
411   - new google.maps.LatLng(37.787767, -122.393987),
412   - new google.maps.LatLng(37.787486, -122.394452),
413   - new google.maps.LatLng(37.786977, -122.395043),
414   - new google.maps.LatLng(37.786583, -122.395552),
415   - new google.maps.LatLng(37.786540, -122.395610),
416   - new google.maps.LatLng(37.786516, -122.395659),
417   - new google.maps.LatLng(37.786378, -122.395707),
418   - new google.maps.LatLng(37.786044, -122.395362),
419   - new google.maps.LatLng(37.785598, -122.394715),
420   - new google.maps.LatLng(37.785321, -122.394361),
421   - new google.maps.LatLng(37.785207, -122.394236),
422   - new google.maps.LatLng(37.785751, -122.394062),
423   - new google.maps.LatLng(37.785996, -122.393881),
424   - new google.maps.LatLng(37.786092, -122.393830),
425   - new google.maps.LatLng(37.785998, -122.393899),
426   - new google.maps.LatLng(37.785114, -122.394365),
427   - new google.maps.LatLng(37.785022, -122.394441),
428   - new google.maps.LatLng(37.784823, -122.394635),
429   - new google.maps.LatLng(37.784719, -122.394629),
430   - new google.maps.LatLng(37.785069, -122.394176),
431   - new google.maps.LatLng(37.785500, -122.393650),
432   - new google.maps.LatLng(37.785770, -122.393291),
433   - new google.maps.LatLng(37.785839, -122.393159),
434   - new google.maps.LatLng(37.782651, -122.400628),
435   - new google.maps.LatLng(37.782616, -122.400599),
436   - new google.maps.LatLng(37.782702, -122.400470),
437   - new google.maps.LatLng(37.782915, -122.400192),
438   - new google.maps.LatLng(37.783137, -122.399887),
439   - new google.maps.LatLng(37.783414, -122.399519),
440   - new google.maps.LatLng(37.783629, -122.399237),
441   - new google.maps.LatLng(37.783688, -122.399157),
442   - new google.maps.LatLng(37.783716, -122.399106),
443   - new google.maps.LatLng(37.783798, -122.399072),
444   - new google.maps.LatLng(37.783997, -122.399186),
445   - new google.maps.LatLng(37.784271, -122.399538),
446   - new google.maps.LatLng(37.784577, -122.399948),
447   - new google.maps.LatLng(37.784828, -122.400260),
448   - new google.maps.LatLng(37.784999, -122.400477),
449   - new google.maps.LatLng(37.785113, -122.400651),
450   - new google.maps.LatLng(37.785155, -122.400703),
451   - new google.maps.LatLng(37.785192, -122.400749),
452   - new google.maps.LatLng(37.785278, -122.400839),
453   - new google.maps.LatLng(37.785387, -122.400857),
454   - new google.maps.LatLng(37.785478, -122.400890),
455   - new google.maps.LatLng(37.785526, -122.401022),
456   - new google.maps.LatLng(37.785598, -122.401148),
457   - new google.maps.LatLng(37.785631, -122.401202),
458   - new google.maps.LatLng(37.785660, -122.401267),
459   - new google.maps.LatLng(37.803986, -122.426035),
460   - new google.maps.LatLng(37.804102, -122.425089),
461   - new google.maps.LatLng(37.804211, -122.424156),
462   - new google.maps.LatLng(37.803861, -122.423385),
463   - new google.maps.LatLng(37.803151, -122.423214),
464   - new google.maps.LatLng(37.802439, -122.423077),
465   - new google.maps.LatLng(37.801740, -122.422905),
466   - new google.maps.LatLng(37.801069, -122.422785),
467   - new google.maps.LatLng(37.800345, -122.422649),
468   - new google.maps.LatLng(37.799633, -122.422603),
469   - new google.maps.LatLng(37.799750, -122.421700),
470   - new google.maps.LatLng(37.799885, -122.420854),
471   - new google.maps.LatLng(37.799209, -122.420607),
472   - new google.maps.LatLng(37.795656, -122.400395),
473   - new google.maps.LatLng(37.795203, -122.400304),
474   - new google.maps.LatLng(37.778738, -122.415584),
475   - new google.maps.LatLng(37.778812, -122.415189),
476   - new google.maps.LatLng(37.778824, -122.415092),
477   - new google.maps.LatLng(37.778833, -122.414932),
478   - new google.maps.LatLng(37.778834, -122.414898),
479   - new google.maps.LatLng(37.778740, -122.414757),
480   - new google.maps.LatLng(37.778501, -122.414433),
481   - new google.maps.LatLng(37.778182, -122.414026),
482   - new google.maps.LatLng(37.777851, -122.413623),
483   - new google.maps.LatLng(37.777486, -122.413166),
484   - new google.maps.LatLng(37.777109, -122.412674),
485   - new google.maps.LatLng(37.776743, -122.412186),
486   - new google.maps.LatLng(37.776440, -122.411800),
487   - new google.maps.LatLng(37.776295, -122.411614),
488   - new google.maps.LatLng(37.776158, -122.411440),
489   - new google.maps.LatLng(37.775806, -122.410997),
490   - new google.maps.LatLng(37.775422, -122.410484),
491   - new google.maps.LatLng(37.775126, -122.410087),
492   - new google.maps.LatLng(37.775012, -122.409854),
493   - new google.maps.LatLng(37.775164, -122.409573),
494   - new google.maps.LatLng(37.775498, -122.409180),
495   - new google.maps.LatLng(37.775868, -122.408730),
496   - new google.maps.LatLng(37.776256, -122.408240),
497   - new google.maps.LatLng(37.776519, -122.407928),
498   - new google.maps.LatLng(37.776539, -122.407904),
499   - new google.maps.LatLng(37.776595, -122.407854),
500   - new google.maps.LatLng(37.776853, -122.407547),
501   - new google.maps.LatLng(37.777234, -122.407087),
502   - new google.maps.LatLng(37.777644, -122.406558),
503   - new google.maps.LatLng(37.778066, -122.406017),
504   - new google.maps.LatLng(37.778468, -122.405499),
505   - new google.maps.LatLng(37.778866, -122.404995),
506   - new google.maps.LatLng(37.779295, -122.404455),
507   - new google.maps.LatLng(37.779695, -122.403950),
508   - new google.maps.LatLng(37.779982, -122.403584),
509   - new google.maps.LatLng(37.780295, -122.403223),
510   - new google.maps.LatLng(37.780664, -122.402766),
511   - new google.maps.LatLng(37.781043, -122.402288),
512   - new google.maps.LatLng(37.781399, -122.401823),
513   - new google.maps.LatLng(37.781727, -122.401407),
514   - new google.maps.LatLng(37.781853, -122.401247),
515   - new google.maps.LatLng(37.781894, -122.401195),
516   - new google.maps.LatLng(37.782076, -122.400977),
517   - new google.maps.LatLng(37.782338, -122.400603),
518   - new google.maps.LatLng(37.782666, -122.400133),
519   - new google.maps.LatLng(37.783048, -122.399634),
520   - new google.maps.LatLng(37.783450, -122.399198),
521   - new google.maps.LatLng(37.783791, -122.398998),
522   - new google.maps.LatLng(37.784177, -122.398959),
523   - new google.maps.LatLng(37.784388, -122.398971),
524   - new google.maps.LatLng(37.784404, -122.399128),
525   - new google.maps.LatLng(37.784586, -122.399524),
526   - new google.maps.LatLng(37.784835, -122.399927),
527   - new google.maps.LatLng(37.785116, -122.400307),
528   - new google.maps.LatLng(37.785282, -122.400539),
529   - new google.maps.LatLng(37.785346, -122.400692),
530   - new google.maps.LatLng(37.765769, -122.407201),
531   - new google.maps.LatLng(37.765790, -122.407414),
532   - new google.maps.LatLng(37.765802, -122.407755),
533   - new google.maps.LatLng(37.765791, -122.408219),
534   - new google.maps.LatLng(37.765763, -122.408759),
535   - new google.maps.LatLng(37.765726, -122.409348),
536   - new google.maps.LatLng(37.765716, -122.409882),
537   - new google.maps.LatLng(37.765708, -122.410202),
538   - new google.maps.LatLng(37.765705, -122.410253),
539   - new google.maps.LatLng(37.765707, -122.410369),
540   - new google.maps.LatLng(37.765692, -122.410720),
541   - new google.maps.LatLng(37.765699, -122.411215),
542   - new google.maps.LatLng(37.765687, -122.411789),
543   - new google.maps.LatLng(37.765666, -122.412373),
544   - new google.maps.LatLng(37.765598, -122.412883),
545   - new google.maps.LatLng(37.765543, -122.413039),
546   - new google.maps.LatLng(37.765532, -122.413125),
547   - new google.maps.LatLng(37.765500, -122.413553),
548   - new google.maps.LatLng(37.765448, -122.414053),
549   - new google.maps.LatLng(37.765388, -122.414645),
550   - new google.maps.LatLng(37.765323, -122.415250),
551   - new google.maps.LatLng(37.765303, -122.415847),
552   - new google.maps.LatLng(37.765251, -122.416439),
553   - new google.maps.LatLng(37.765204, -122.417020),
554   - new google.maps.LatLng(37.765172, -122.417556),
555   - new google.maps.LatLng(37.765164, -122.418075),
556   - new google.maps.LatLng(37.765153, -122.418618),
557   - new google.maps.LatLng(37.765136, -122.419112),
558   - new google.maps.LatLng(37.765129, -122.419378),
559   - new google.maps.LatLng(37.765119, -122.419481),
560   - new google.maps.LatLng(37.765100, -122.419852),
561   - new google.maps.LatLng(37.765083, -122.420349),
562   - new google.maps.LatLng(37.765045, -122.420930),
563   - new google.maps.LatLng(37.764992, -122.421481),
564   - new google.maps.LatLng(37.764980, -122.421695),
565   - new google.maps.LatLng(37.764993, -122.421843),
566   - new google.maps.LatLng(37.764986, -122.422255),
567   - new google.maps.LatLng(37.764975, -122.422823),
568   - new google.maps.LatLng(37.764939, -122.423411),
569   - new google.maps.LatLng(37.764902, -122.424014),
570   - new google.maps.LatLng(37.764853, -122.424576),
571   - new google.maps.LatLng(37.764826, -122.424922),
572   - new google.maps.LatLng(37.764796, -122.425375),
573   - new google.maps.LatLng(37.764782, -122.425869),
574   - new google.maps.LatLng(37.764768, -122.426089),
575   - new google.maps.LatLng(37.764766, -122.426117),
576   - new google.maps.LatLng(37.764723, -122.426276),
577   - new google.maps.LatLng(37.764681, -122.426649),
578   - new google.maps.LatLng(37.782012, -122.404200),
579   - new google.maps.LatLng(37.781574, -122.404911),
580   - new google.maps.LatLng(37.781055, -122.405597),
581   - new google.maps.LatLng(37.780479, -122.406341),
582   - new google.maps.LatLng(37.779996, -122.406939),
583   - new google.maps.LatLng(37.779459, -122.407613),
584   - new google.maps.LatLng(37.778953, -122.408228),
585   - new google.maps.LatLng(37.778409, -122.408839),
586   - new google.maps.LatLng(37.777842, -122.409501),
587   - new google.maps.LatLng(37.777334, -122.410181),
588   - new google.maps.LatLng(37.776809, -122.410836),
589   - new google.maps.LatLng(37.776240, -122.411514),
590   - new google.maps.LatLng(37.775725, -122.412145),
591   - new google.maps.LatLng(37.775190, -122.412805),
592   - new google.maps.LatLng(37.774672, -122.413464),
593   - new google.maps.LatLng(37.774084, -122.414186),
594   - new google.maps.LatLng(37.773533, -122.413636),
595   - new google.maps.LatLng(37.773021, -122.413009),
596   - new google.maps.LatLng(37.772501, -122.412371),
597   - new google.maps.LatLng(37.771964, -122.411681),
598   - new google.maps.LatLng(37.771479, -122.411078),
599   - new google.maps.LatLng(37.770992, -122.410477),
600   - new google.maps.LatLng(37.770467, -122.409801),
601   - new google.maps.LatLng(37.770090, -122.408904),
602   - new google.maps.LatLng(37.769657, -122.408103),
603   - new google.maps.LatLng(37.769132, -122.407276),
604   - new google.maps.LatLng(37.768564, -122.406469),
605   - new google.maps.LatLng(37.767980, -122.405745),
606   - new google.maps.LatLng(37.767380, -122.405299),
607   - new google.maps.LatLng(37.766604, -122.405297),
608   - new google.maps.LatLng(37.765838, -122.405200),
609   - new google.maps.LatLng(37.765139, -122.405139),
610   - new google.maps.LatLng(37.764457, -122.405094),
611   - new google.maps.LatLng(37.763716, -122.405142),
612   - new google.maps.LatLng(37.762932, -122.405398),
613   - new google.maps.LatLng(37.762126, -122.405813),
614   - new google.maps.LatLng(37.761344, -122.406215),
615   - new google.maps.LatLng(37.760556, -122.406495),
616   - new google.maps.LatLng(37.759732, -122.406484),
617   - new google.maps.LatLng(37.758910, -122.406228),
618   - new google.maps.LatLng(37.758182, -122.405695),
619   - new google.maps.LatLng(37.757676, -122.405118),
620   - new google.maps.LatLng(37.757039, -122.404346),
621   - new google.maps.LatLng(37.756335, -122.403719),
622   - new google.maps.LatLng(37.755503, -122.403406),
623   - new google.maps.LatLng(37.754665, -122.403242),
624   - new google.maps.LatLng(37.753837, -122.403172),
625   - new google.maps.LatLng(37.752986, -122.403112),
626   - new google.maps.LatLng(37.751266, -122.403355)
627   - ];
628   - }
  344 + function getCircle() {
  345 + return {
  346 + path: google.maps.SymbolPath.CIRCLE,
  347 + fillColor: 'red',
  348 + fillOpacity: .8,
  349 + scale: 10,
  350 + strokeColor: 'white',
  351 + strokeWeight: .5
  352 + };
  353 + }
629 354 </script>
630 355  
631 356 <!-- Calling google maps API using the function defined above. -->
632 357 <script async defer
633   - src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDAXDHeZXEv4H4ZnThVDxpyAuxVpzOcj_U&callback=initMap&libraries=visualization">
  358 + src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDAXDHeZXEv4H4ZnThVDxpyAuxVpzOcj_U&callback=initMap&libraries=visualization,places">
634 359 </script>
635 360  
636   -
637 361 <!-- Buttons at the bottom of the map -->
638 362 <div class="container">
639 363 <div class="row">
640   - <!-- Status -->
641   - <div class="col-xs-4">
642   - <!-- Title: Current Area -->
643   - <div>
644   - Current Area:
645   - </div>
646   - <!-- Icons + Status -->
647   - <div>
648   - 80% Safe
649   - </div>
650   - </div>
651 364 <!-- Emergency -->
652   - <div class="col-xs-4">
653   - <button type="button" class="btn btn-danger btn-block" onclick="emergency()">Emergency</button>
  365 + <div class="col-xs-6">
  366 + <button type="button" class="btn btn-danger btn-block text-center" onclick="emergency()">Emergency</button>
654 367 </div>
655 368 <!-- Open Map -->
656   - <div class="col-xs-4">
657   - <button type="button" class="btn btn-default btn-block" onclick="todo()">Open in Map</button>
  369 + <div class="col-xs-6">
  370 + <button type="button" class="btn btn-default btn-block text-center" onclick="openInMaps()">Open in Map</button>
658 371 </div>
659 372 </div>
660 373 </div>
661 374  
662 375 </div>
663 376  
664   -<script>
665   - function emergency(){
  377 + <script>
  378 + function emergency() {
  379 + var res = confirm("Would you like to call 911?");
  380 + if( res == true ) {
  381 + alert("calling 911...");// <a href="tel:+1-911">call 911</a>
  382 + }
  383 + else {
  384 + alert("Please stay safe!");
  385 + }
  386 + }
666 387  
667   - var res = confirm("You are in a dangerous area. Would you like to call 911?");
668   - if( res == true ) {
669   - alert("calling 911...");// <a href="tel:+1-911">call 911</a>
670   - }
671   - else {
672   - alert("Please stay safe!");
673   - }
674   - }
  388 + function openInMaps(){
675 389  
676   - function todo() {
677   - alert("This button is brought to you by the wonderful Wizard of Oz.");
678   - }
679   - </script>
  390 +
  391 +
  392 + if(window.mobilecheck() == true){
  393 + var str = 'comgooglemaps://?saddr='.concat((document.getElementById('origin').value).concat('&daddr=').concat(document.getElementById('dest').value).concat('&directionsmode').concat(localStorage.getItem('mode')));
  394 + window.location = str;
  395 + } else {
  396 + alert("This feature only works for mobile devices");
  397 + }
  398 +
  399 +
  400 + }
  401 +
  402 + function todo() {
  403 + alert("This button is brought to you by the wonderful Wizard of Oz.");
  404 + }
  405 +
  406 +
  407 + window.mobilecheck = function() {
  408 + var check = false;
  409 + (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);
  410 + return check;
  411 + };
  412 + </script>
680 413  
681 414 </body>
682 415 </html>
views/pages/login.ejs View file @ ef269e4
... ... @@ -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