Google maps - how to get building's polygon coordinates from address?
Solution 1
You can convert the address to geographic coordinates by the use of the Google Geocoding API.
https://maps.googleapis.com/maps/api/geocode/json?address=SOME_ADDRESS&key=YOUR_API_KEY
Then, you can use Python and a styled static map to obtain the polygon of the building (in pixel coordinates) at some location:
import numpy as np
from requests.utils import quote
from skimage.measure import find_contours, points_in_poly, approximate_polygon
from skimage import io
from skimage import color
from threading import Thread
center_latitude = None ##put latitude here
center_longitude = None ##put longitude here
mapZoom = str(20)
midX = 300
midY = 300
# Styled google maps url showing only the buildings
safeURL_Style = quote('feature:landscape.man_made|element:geometry.stroke|visibility:on|color:0xffffff|weight:1')
urlBuildings = "http://maps.googleapis.com/maps/api/staticmap?center=" + str_Center + "&zoom=" + mapZoom + "&format=png32&sensor=false&size=" + str_Size + "&maptype=roadmap&style=visibility:off&style=" + safeURL_Style
mainBuilding = None
imgBuildings = io.imread(urlBuildings)
gray_imgBuildings = color.rgb2gray(imgBuildings)
# will create inverted binary image
binary_imageBuildings = np.where(gray_imgBuildings > np.mean(gray_imgBuildings), 0.0, 1.0)
contoursBuildings = find_contours(binary_imageBuildings, 0.1)
for n, contourBuilding in enumerate(contoursBuildings):
if (contourBuilding[0, 1] == contourBuilding[-1, 1]) and (contourBuilding[0, 0] == contourBuilding[-1, 0]):
# check if it is inside any other polygon, so this will remove any additional elements
isInside = False
skipPoly = False
for othersPolygon in contoursBuildings:
isInside = points_in_poly(contourBuilding, othersPolygon)
if all(isInside):
skipPoly = True
break
if skipPoly == False:
center_inside = points_in_poly(np.array([[midX, midY]]), contourBuilding)
if center_inside:
# approximate will generalize the polygon
mainBuilding = approximate_polygon(contourBuilding, tolerance=2)
print(mainBuilding)
Now, you can convert the pixel coordinates to latitude and longitude by the use of little JavaScript, and the Google Maps API:
function point2LatLng(point, map) {
var topRight = map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast());
var bottomLeft = map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest());
var scale = Math.pow(2, map.getZoom());
var worldPoint = new google.maps.Point(point.x / scale + bottomLeft.x, point.y / scale + topRight.y);
return map.getProjection().fromPointToLatLng(worldPoint);
}
var convertedPointsMain = [];
for (var i = 0; i < pxlMainPolygons[p].length; i++) {
var conv_point = {
x: Math.round(pxlMainPolygons[p][i][1]),
y: Math.round(pxlMainPolygons[p][i][0])
};
convertedPointsMain[i] = point2LatLng(conv_point, map);
}
console.log(convertedPointsMain);
Solution 2
Might I humbly suggest you use OpenStreetMaps for this instead ?
It's a lot easier, because then you can use the OverPass API.
However, polygons might not match with google-maps or with state survey.
The latter also holds true if you would use google-maps.
// https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL
private static string GetOqlBuildingQuery(int distance, decimal latitude, decimal longitude)
{
System.Globalization.NumberFormatInfo nfi = new System.Globalization.NumberFormatInfo()
{
NumberGroupSeparator = "",
NumberDecimalSeparator = ".",
CurrencyGroupSeparator = "",
CurrencyDecimalSeparator = ".",
CurrencySymbol = ""
};
// [out: json];
// way(around:25, 47.360867, 8.534703)["building"];
// out ids geom meta;
string oqlQuery = @"[out:json];
way(around:" + distance.ToString(nfi) + ", "
+ latitude.ToString(nfi) + ", " + longitude.ToString(nfi)
+ @")[""building""];
out ids geom;"; // ohne meta - ist minimal
return oqlQuery;
}
public static System.Collections.Generic.List<Wgs84Point> GetWgs84PolygonPoints(int distance, decimal latitude, decimal longitude)
{
string[] overpass_services = new string[] {
"http://overpass.osm.ch/api/interpreter",
"http://overpass.openstreetmap.fr/api/interpreter",
"http://overpass-api.de/api/interpreter",
"http://overpass.osm.rambler.ru/cgi/interpreter",
// "https://overpass.osm.vi-di.fr/api/interpreter", // offline...
};
// string url = "http://overpass.osm.ch/api/interpreter";
// string url = "http://overpass-api.de/api/interpreter";
string url = overpass_services[s_rnd.Next(0, overpass_services.Length)];
System.Collections.Specialized.NameValueCollection reqparm = new System.Collections.Specialized.NameValueCollection();
reqparm.Add("data", GetOqlBuildingQuery(distance, latitude, longitude));
string resp = PostRequest(url, reqparm);
// System.IO.File.WriteAllText(@"D:\username\Documents\visual studio 2017\Projects\TestPlotly\TestSpatial\testResponse.json", resp, System.Text.Encoding.UTF8);
// System.Console.WriteLine(resp);
// string resp = System.IO.File.ReadAllText(@"D:\username\Documents\visual studio 2017\Projects\TestPlotly\TestSpatial\testResponse.json", System.Text.Encoding.UTF8);
System.Collections.Generic.List<Wgs84Point> ls = null;
Overpass.Building.BuildingInfo ro = Overpass.Building.BuildingInfo.FromJson(resp);
if (ro != null && ro.Elements != null && ro.Elements.Count > 0 && ro.Elements[0].Geometry != null)
{
ls = new System.Collections.Generic.List<Wgs84Point>();
for (int i = 0; i < ro.Elements[0].Geometry.Count; ++i)
{
ls.Add(new Wgs84Point(ro.Elements[0].Geometry[i].Latitude, ro.Elements[0].Geometry[i].Longitude, i));
} // Next i
} // End if (ro != null && ro.Elements != null && ro.Elements.Count > 0 && ro.Elements[0].Geometry != null)
return ls;
} // End Function GetWgs84Points
Solution 3
I've been working on this for hours, the closest I have come is finding a request uri that returns a result with a polygon in it. I believe it specifies the building(boundary) by editids
parameter. We just need a way to get the current editids from a building(boundary).
The URI I have is:
https://www.google.com/mapmaker?hl=en&gw=40&output=jsonp&ll=38.934911%2C-92.329359&spn=0.016288%2C0.056477&z=14&mpnum=0&vpid=1354239392511&editids=nAlkfrzSpBMuVg-hSJ&xauth=YOUR_XAUTH_HERE&geowiki_client=mapmaker&hl=en
Part of the result has what is needed:
"polygon":[{"gnew":{"loop":[{"vertex":[{"lat_e7":389364691,"lng_e7":-923341133},{"lat_e7":389362067,"lng_e7":-923342783},{"lat_e7":389361075,"lng_e7":-923343356},{"lat_e7":389360594,"lng_e7":-923342477},
Solution 4
I was intrigued on this problem and wrote a solution to it. See my github project.
Comments
-
Konstantin Chernov almost 2 years
How to implement the following:
- User defines an address
- User defines a color
- Service searches for a corresponding building on the google map
- Service fills the found building on the map with the color
I know how to:
1.find lat/long of the address
2.draw the polygon
So, to do the task I need to get polygon coordinates of building from address. How to?
-
Konstantin Chernov over 11 yearsRaymond, thx for sharing! I'm still looking for a solution. How to generate such an uri andhow to extract polygon data from response? Is it possible?