Add "Search Area" outline onto google maps result

Last year, Google added search area outlines to its offerings as reported in many places, and visible on Google maps itself. For example here, and reported here and here.

To be explicit, this is when google adds an outline relating to the search query. If you were to search for a town, a post code, or a zip code, Google will highlight this region of the map. Example:

90210 Search on Google Maps

This was apparently not available via the API, and only through Google's own web properties.

Recently, I noticed some other domains using this functionality, for example on Twitter.

Is there a seperate API Twitter and other large organisations are using? Has this functionality been added, but is as yet undocumented? Or have I simply missed the announcement and can't find any docs?


As there is no yet solution offered by the Maps API and manually filling in coordinates is nobody's business, here's a gem of an alternative. Found this answer on the GIS website--absolute lifesaver (would've saved jurihandl a LOT of minutes drawing Calgary, above ;D):

You can get polygon coordinates in json for using with googlemaps using openstreetmap. Go to http://nominatim.openstreetmap.org/. Search a place like "San Francisco, CA"

Click on "Details"

Look for OSM ID and copy it (control+c), example: 2018776

Paste the ID http://polygons.openstreetmap.fr/index.py and download the JSON file

Source: GIS


The outlines you see there come from twitter, they must have stored them.

Take a look at the json-file that is requested when you call the twitter-page: http://api.twitter.com/1/geo/id/c3f37afa9efcf94b.json

I've tried it, geometry.coordinates[0][0] defines a fine polygon(guess the outline for Austin).

When you try it, notice that the order of the pair is lng,lat not lat,lng

So the twitter-geo-API may be a good start for implementing the outlines, fortunately twitter supports JSONP for a clientside solution.

See an example: http://jsfiddle.net/doktormolle/MRYm3/

<edit>

the twitter-API has been changed, the example didn't work anymore(authentication is required)

</edit>


I found a pretty good solution to draw the city border.

Here is a pretty cool tool, where you can draw the city border. You can be as exact as you like to be: http://www.birdtheme.org/useful/v3tool.html

On the right side you get a live preview of your code. You can choose between KML and javascript. Switch to javascript. Then copy your coordinates.

Here's the full site where you can see the border of Brussels (Europe).

<!DOCTYPE html>
<html lang="en-US">
 <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>::Maps ::</title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false&key=AIzaSyD0X4v7eqMFcWCR-VZAJwEMfb47id9IZao"></script>
    <script type="text/javascript">
        var map;

        //COORDS
        var brussels = [
        new google.maps.LatLng(50.835866,4.258575),
        new google.maps.LatLng(50.818083,4.244499),
        new google.maps.LatLng(50.811358,4.276428),
        new google.maps.LatLng(50.813094,4.302177),
        new google.maps.LatLng(50.773162,4.338226),
        new google.maps.LatLng(50.764259,4.384918),
        new google.maps.LatLng(50.793132,4.482422),
        new google.maps.LatLng(50.810274,4.450836),
        new google.maps.LatLng(50.821120,4.476585),
        new google.maps.LatLng(50.852342,4.462852),
        new google.maps.LatLng(50.866861,4.421310),
        new google.maps.LatLng(50.895021,4.430580),
        new google.maps.LatLng(50.911692,4.413757),
        new google.maps.LatLng(50.912342,4.395561),
        new google.maps.LatLng(50.898486,4.377708),
        new google.maps.LatLng(50.900868,4.328957),
        new google.maps.LatLng(50.889174,4.293251),
        new google.maps.LatLng(50.880294,4.297028),
        new google.maps.LatLng(50.861878,4.279175),
        new google.maps.LatLng(50.855593,4.288788),
        new google.maps.LatLng(50.837817,4.282608),
        new google.maps.LatLng(50.835866,4.259605)
        ];

        $(document).ready(function () {
            //WHERE TO CENTER YOUR MAP
            var latlng = new google.maps.LatLng(50.834999,4.387665);
            var myOptions = {
                zoom: 10,
                center: latlng,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };
            map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

            var BrusselsHightlight;

            //DRAW THE POLYGON OR POLYLINE
            BrusselsHightlight = new google.maps.Polygon({
                paths: brussels,
                strokeColor: "#6666FF",
                strokeOpacity: 0.8,
                strokeWeight: 2,
                fillColor: "#6666FF",
                fillOpacity: 0.35
            });
            BrusselsHightlight.setMap(map);

        });

    </script>
    <style type="text/css">
        html,body { height: 100%; margin: 0px; padding: 0px; }
        #map_canvas {
            width:600px;
            height:400px;
        }       
    </style>
</head>
<body >
<div id="map_canvas">

</div>
<!--main-->
<div id="map_cord"></div>
</body> 
</html>

This worked really great for me.


twitter api geo endpoint will NOT give you city boundary,

what they provide you is ONLY bounding box with 5 point(lat, long)

this is what I get from twitter api geo for San Francisco enter image description here


It's a bit of a pain in the, but you can use BING maps api to get detailed polygons. They make it pretty difficult to figure out from their api but this blog post helped me out. You have to decompress the data once they send it to you. They are drawing the shape using bing maps api in that post but I am converting it into geojson. This is how I am doing it.

 function getBingMapsGeometry(latitude, longitude, entityType) {
   //api docs: https://docs.microsoft.com/en-us/bingmaps/spatial-data-services/geodata-api
        let baseUrl =
                "http://platform.bing.com/geo/spatial/v1/public/geodata?SpatialFilter=",
            levelOfDetail = 3,
            getAllPolygons = 1,
            getEntityMetadata = 1,
            culture = "en-us",
            userRegion = "US",
            preferCuratedPolygons = 1,
            responseFormat = "json",
            key = "your_api_key";
        return fetch(`${baseUrl}GetBoundary(${latitude},${longitude},${levelOfDetail},'${entityType}',${getAllPolygons},${getEntityMetadata},'${culture}','${userRegion}')&PreferCuratedPolygons=${preferCuratedPolygons}&$format=${responseFormat}&key=${key}`
        ).then(res => {
            res.json().then(result => {
                var entity = result.d.results[0];
                if (!entity) {
                    alert("no results");
                    return;
                }
            // much help from this post: https://blogs.bing.com/maps/2013/06/25/retrieving-boundaries-from-the-bing-spatial-data-services-preview
                var entityMetadata = entity.EntityMetadata;
                entityMetadata.name = entity.Name.EntityName;
                var primitives = entity.Primitives;
                var boundaryVertices = null;
                var numOfVertices = 0;
                var multyiPolygonArray = [];
                var singlePolygonArray;
                for (var i = 0; i < primitives.length; i++) {
                    var ringStr = primitives[i].Shape;
                    var ringArray = ringStr.split(",");

                    for (var j = 1; j < ringArray.length; j++) {
                        var polygonArray = new Array();
                        var array = ParseEncodedValue(ringArray[j]);

                        if (array.length > numOfVertices) {
                            numOfVertices = array.length;
                            boundaryVertices = array;
                        }
                        polygonArray.push(array);
                        if (ringArray.length > 1)
                            multyiPolygonArray.push(polygonArray);
                        else singlePolygonArray = polygonArray;
                    }
                }
                let geojson = {
                    type: "FeatureCollection",
                    features: [
                        {
                            type: "Feature",
                            properties: entityMetadata,
                            geometry: {
                                type: singlePolygonArray
                                    ? "Polygon"
                                    : "MultiPolygon",
                                coordinates: singlePolygonArray
                                    ? singlePolygonArray
                                    : multyiPolygonArray
                            }
                        }
                    ]
                };
return geojson;
            });
        });
    }

 function ParseEncodedValue(value) {
        var safeCharacters =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
        var list = new Array();
        var index = 0;
        var xsum = 0;
        var ysum = 0;
        var max = 4294967296;

        while (index < value.length) {
            var n = 0;
            var k = 0;

            while (1) {
                if (index >= value.length) {
                    return null;
                }
                var b = safeCharacters.indexOf(value.charAt(index++));
                if (b == -1) {
                    return null;
                }
                var tmp = (b & 31) * Math.pow(2, k);

                var ht = tmp / max;
                var lt = tmp % max;

                var hn = n / max;
                var ln = n % max;

                var nl = (lt | ln) >>> 0;
                n = (ht | hn) * max + nl;
                k += 5;
                if (b < 32) break;
            }

            var diagonal = parseInt((Math.sqrt(8 * n + 5) - 1) / 2);
            n -= (diagonal * (diagonal + 1)) / 2;
            var ny = parseInt(n);
            var nx = diagonal - ny;
            nx = (nx >> 1) ^ -(nx & 1);
            ny = (ny >> 1) ^ -(ny & 1);
            xsum += nx;
            ysum += ny;
            var lat = ysum * 0.00001;
            var lon = xsum * 0.00001;
            list.push([lon, lat]);
        }
        return list;
    }