├── LICENSE ├── README.md ├── demo1.png ├── demo2.png ├── index.css ├── index.html ├── index.js └── screenshot.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Modus Create 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![No longer maintained](https://img.shields.io/badge/Maintenance-OFF-red.svg) 2 | ### [DEPRECATED] This repository is no longer maintained 3 | > While this project is fully functional, the dependencies are no longer up to date. You are still welcome to explore, learn, and use the code provided here. 4 | > 5 | > Modus is dedicated to supporting the community with innovative ideas, best-practice patterns, and inspiring open source solutions. Check out the latest [Modus Labs](https://labs.moduscreate.com?utm_source=github&utm_medium=readme&utm_campaign=deprecated) projects. 6 | 7 | [![Modus Labs](https://res.cloudinary.com/modus-labs/image/upload/h_80/v1531492623/labs/logo-black.png)](https://labs.moduscreate.com?utm_source=github&utm_medium=readme&utm_campaign=deprecated) 8 | 9 | --- 10 | 11 | # Polygon Experiment 12 | 13 | Experiments with placing a marker around the center of a polygon. This repo has an accompanying [blog post](http://moduscreate.com/placing-markers-inside-polygons-with-google-maps/). 14 | 15 | ![demo](screenshot.png) 16 | 17 | Find approximate center point of an arbitrary polygon on Google Maps. Process: 18 | 19 | * Add a `getBoundingBox` method to `google.maps.Polygon.prototype` which returns a LatLngBounds object (rectangle) that entirely contains an arbitrarily complex polygon 20 | * Get the center of that bounding box 21 | * If the center of the bounding box is within the area of the polygon, put the marker there 22 | * If the center of the bounding box is not within the area of the polygon then: 23 | * Work out the height of the bounding box 24 | * Look at points North, East, South and West of the center at 5% increments of the total height and width of the bounding box 25 | * If any of those points is within the area of the polygon, place the marker there and stop looking 26 | 27 | This may not be foolproof but should get a point within the polygon that's good enough. As this moves up and down the bounding box 28 | looking for points within the polygon at 5% height increments, it could miss a very thin slice of the polygon that crosses the 29 | center line and never find a point... could fix this by using 1% increments and a 50 loop count for higher search "resolution" 30 | but lower performance. 31 | 32 | ## Examples 33 | 34 | The following show two sample polygons where the centroid (blue) of the bounding box (shown as black outline) falls outside the polygon. 35 | The algotithm found the nearest point North, South, East or West and decided on a final placement where the red marker lies. 36 | 37 | ![demo1](demo1.png) 38 | 39 | This example selected the point due East of the centroid because the polygon is much wider than it is high, so the East / West search 40 | increments were bigger than the North / South ones, so the Eastern border was discovered first. 41 | 42 | ![demo2](demo2.png) 43 | 44 | ## Notes 45 | 46 | * You need to supply a Google Maps API key and set this in the link to the Google Maps API JavaScript in `index.html` 47 | * Because we are using `google.maps.geometry` functions, the link to get the Google Maps API JavaSCript needs to include `&libraries=geometry` (see `index.html`) 48 | 49 | ## Other Ways of Doing This 50 | 51 | There are other possible algorithms for getting similar results: 52 | 53 | * MapBox has one [here](https://github.com/mapbox/polylabel/blob/master/index.js) with a supporting [blog post] (https://www.mapbox.com/blog/polygon-center/) 54 | * [Discussion on calculating centroid](http://mathcentral.uregina.ca/qq/database/qq.09.07/h/david7.html) 55 | -------------------------------------------------------------------------------- /demo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModusCreateOrg/google-maps-polygon-center-approximation-blog/8faff17af84f367710f8c25bd37ffd7b7281c986/demo1.png -------------------------------------------------------------------------------- /demo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModusCreateOrg/google-maps-polygon-center-approximation-blog/8faff17af84f367710f8c25bd37ffd7b7281c986/demo2.png -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | #map { 2 | height: 600px; 3 | position: relative; 4 | width: 100%; 5 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Google Map Shapes Tester 6 | 7 | 8 | 9 |

Google Maps Shape Tester

10 |
11 |
12 |
13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var app = { 4 | // Utility function to make a polygon with some standard properties set 5 | makePolygon: function(paths, color) { 6 | return (new google.maps.Polygon({ 7 | paths: paths, 8 | strokeColor: color, 9 | strokeOpacity: 0.8, 10 | strokeWeight: 2, 11 | fillColor: color, 12 | fillOpacity: 0.35 13 | })); 14 | }, 15 | 16 | // Run on page load 17 | initialize: function() { 18 | var map, 19 | polygons = []; 20 | 21 | // Add useful missing method - credit to http://stackoverflow.com/a/13772082/5338708 22 | google.maps.Polygon.prototype.getBoundingBox = function() { 23 | var bounds = new google.maps.LatLngBounds(); 24 | 25 | this.getPath().forEach(function(element,index) { 26 | bounds.extend(element) 27 | }); 28 | 29 | return(bounds); 30 | }; 31 | 32 | // Add center calculation method 33 | google.maps.Polygon.prototype.getApproximateCenter = function() { 34 | var boundsHeight = 0, 35 | boundsWidth = 0, 36 | centerPoint, 37 | heightIncr = 0, 38 | maxSearchLoops, 39 | maxSearchSteps = 10, 40 | n = 1, 41 | northWest, 42 | polygonBounds = this.getBoundingBox(), 43 | testPos, 44 | widthIncr = 0; 45 | 46 | // Get polygon Centroid 47 | centerPoint = polygonBounds.getCenter(); 48 | 49 | if (google.maps.geometry.poly.containsLocation(centerPoint, this)) { 50 | // Nothing to do Centroid is in polygon use it as is 51 | return centerPoint; 52 | } else { 53 | maxSearchLoops = maxSearchSteps / 2; 54 | 55 | // Calculate NorthWest point so we can work out height of polygon NW->SE 56 | northWest = new google.maps.LatLng(polygonBounds.getNorthEast().lat(), polygonBounds.getSouthWest().lng()); 57 | 58 | // Work out how tall and wide the bounds are and what our search increment will be 59 | boundsHeight = google.maps.geometry.spherical.computeDistanceBetween(northWest, polygonBounds.getSouthWest()); 60 | heightIncr = boundsHeight / maxSearchSteps; 61 | boundsWidth = google.maps.geometry.spherical.computeDistanceBetween(northWest, polygonBounds.getNorthEast()); 62 | widthIncr = boundsWidth / maxSearchSteps; 63 | 64 | // Expand out from Centroid and find a point within polygon at 0, 90, 180, 270 degrees 65 | for (; n <= maxSearchLoops; n++) { 66 | // Test point North of Centroid 67 | testPos = google.maps.geometry.spherical.computeOffset(centerPoint, (heightIncr * n), 0); 68 | if (google.maps.geometry.poly.containsLocation(testPos, this)) { 69 | break; 70 | } 71 | 72 | // Test point East of Centroid 73 | testPos = google.maps.geometry.spherical.computeOffset(centerPoint, (widthIncr * n), 90); 74 | if (google.maps.geometry.poly.containsLocation(testPos, this)) { 75 | break; 76 | } 77 | 78 | // Test point South of Centroid 79 | testPos = google.maps.geometry.spherical.computeOffset(centerPoint, (heightIncr * n), 180); 80 | if (google.maps.geometry.poly.containsLocation(testPos, this)) { 81 | break; 82 | } 83 | 84 | // Test point West of Centroid 85 | testPos = google.maps.geometry.spherical.computeOffset(centerPoint, (widthIncr * n), 270); 86 | if (google.maps.geometry.poly.containsLocation(testPos, this)) { 87 | break; 88 | } 89 | } 90 | 91 | return(testPos); 92 | } 93 | }; 94 | 95 | // Set up map around Chicago 96 | map = new google.maps.Map(document.getElementById('map'), { 97 | center: { lat: 41.83771, lng: -87.85090 }, 98 | zoom: 11 99 | }); 100 | 101 | // Draw sample polygons 102 | polygons.push(this.makePolygon([ 103 | { lat: 41.78500, lng: -87.75133 }, 104 | { lat: 41.77681, lng: -87.87836 }, 105 | { lat: 41.80138, lng: -87.92780 }, 106 | { lat: 41.77988, lng: -87.95527 }, 107 | { lat: 41.83208, lng: -87.95801 }, 108 | { lat: 41.83208, lng: -87.94154 }, 109 | { lat: 41.81673, lng: -87.88866 }, 110 | { lat: 41.81417, lng: -87.78773 }, 111 | { lat: 41.87607, lng: -87.77056 }, 112 | { lat: 41.78500, lng: -87.75133 } 113 | ], 114 | '#FF0000' 115 | )); 116 | 117 | polygons.push(app.makePolygon([ 118 | { lat: 41.739921, lng: -88.047180 }, 119 | { lat: 41.801887, lng: -88.074646 }, 120 | { lat: 41.804958, lng: -88.099365 } 121 | ], 122 | '#00FF00' 123 | )); 124 | 125 | polygons.push(app.makePolygon([ 126 | { lat: 41.961899, lng: -88.119965 }, 127 | { lat: 41.940962, lng: -87.990189 }, 128 | { lat: 41.884244, lng: -88.022461 }, 129 | { lat: 41.878620, lng: -88.060226 }, 130 | { lat: 41.934833, lng: -88.095932 } 131 | ], 132 | '#0000FF' 133 | )); 134 | 135 | polygons.push(app.makePolygon([ 136 | { lat: 41.902644, lng: -87.948303 }, 137 | { lat: 41.952198, lng: -87.920837 }, 138 | { lat: 41.933811, lng: -87.878265 }, 139 | { lat: 41.963942, lng: -87.829514 }, 140 | { lat: 41.975173, lng: -87.785568 }, 141 | { lat: 41.900600, lng: -87.837067 }, 142 | { lat: 41.945559, lng: -87.726517 }, 143 | { lat: 41.877598, lng: -87.629700 } 144 | ], 145 | '#FFFF00' 146 | )); 147 | 148 | polygons.push(app.makePolygon([ 149 | { lat: 41.769119, lng: -88.196182 }, 150 | { lat: 41.716349, lng: -88.193436 }, 151 | { lat: 41.762973, lng: -88.117218 } 152 | ], 153 | '#FF7F50' 154 | )); 155 | 156 | polygons.push(app.makePolygon([ 157 | { lat: 41.842311, lng: -87.680511 }, 158 | { lat: 41.852541, lng: -87.621460 }, 159 | { lat: 41.706610, lng: -87.622833 }, 160 | { lat: 41.706610, lng: -88.002548 }, 161 | { lat: 41.746069, lng: -87.968903 }, 162 | { lat: 41.726599, lng: -87.916718 }, 163 | { lat: 41.741971, lng: -87.677078 } 164 | ], 165 | '#000000' 166 | )); 167 | 168 | polygons.push(app.makePolygon([ 169 | { lat: 41.877086, lng: -88.143311 }, 170 | { lat: 41.884244, lng: -88.092499 }, 171 | { lat: 41.836172, lng: -88.088379 }, 172 | { lat: 41.839753, lng: -88.137817 } 173 | ], 174 | '#800080' 175 | )); 176 | 177 | // Put sample polygons on the map with marker at approximated center 178 | polygons.forEach(function(poly) { 179 | poly.setMap(map); 180 | new google.maps.Marker({ 181 | position: poly.getApproximateCenter(), 182 | map: map 183 | }); 184 | }); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModusCreateOrg/google-maps-polygon-center-approximation-blog/8faff17af84f367710f8c25bd37ffd7b7281c986/screenshot.png --------------------------------------------------------------------------------