📜 ⬆️ ⬇️

Is it possible to automatically solve the Mercator puzzle from Google?

Recently there was an article about a cool cartographic puzzle from Google . After I spent about 20 minutes trying to figure it out, I wondered if I could do it automatically?



The first thing to do is get all the polygons. After reading the source code of the page and opening the developer console, the following code was born:
for (polyIdx=0;i<polys.length;polyIdx++){ var paths = polys[polyIdx].getPaths().getArray(); var arMultiPolygons=[]; for (j=0;j<paths.length;j++){ var arPolygons=[]; for (i=0;i<paths[j].length;i++) { arPolygons.push([paths[j].getArray()[i].lng(),paths[j].getArray()[i].lat()]); } arMultiPolygons.push(p) } } 

In the array polys - all polygons are stored. Then it is easy to get the coordinates of all the vertices of the polygons, but you need not forget that all the same it is MultiPolygons . Next you need to put these polygons in the database. Solution: selenium , which allows js code to be executed. Base - PostgreSQL + Postgis.
')
 from selenium import webdriver from shapely.geometry import polygon,multipolygon from shapely.wkt import loads import psycopg2 con = psycopg2.connect(database="WB",user='postgres',password='pass', host='127.0.0.1', port='5432') cur = con.cursor() #  ur.execute("create table googlePolygons ( idx integer, googlegeom geometry );") browser = webdriver.Firefox() browser.get("http://gmaps-samples.googlecode.com/svn/trunk/poly/puzzledrag.html") polysCount = browser.execute_script('return polys.length;') for iPoly in xrange(polysCount): element = browser.execute_script('var paths = polys[%s].getPaths().getArray();var arMultiPolygons=[];' 'for (j=0;j<paths.length;j++){ var arPolygons=[];' 'for (i=0;i<paths[j].length;i++) ' '{arPolygons.push([paths[j].getArray()[i].lng(),paths[j].getArray()[i].lat()]);} arMultiPolygons.push(p)}' 'return arMultiPolygons;'% str(iPoly)) polygons = [] #    for i in element: polygons.append(polygon.Polygon(i)) #   cur.execute("insert into googlePolygons (googlegeom,idx) values (geomfromewkt('"+multipolygon.MultiPolygon(polygons).wkt+"')," + str(iPoly)+");") con.commit() 

Here a wonderful shapely library was used, which allows you to work with geo-objects right in python. Then I thought that in general, why then cling to the base, if everything is so cool. But the output was bad - except for how to serialize objects this library did not suit me. The first difference that caught my eye is different centroids with post-gap in cases where there are more than one polygon. And since I was going to compare the areas of countries, the algorithm would be as follows:
And the algorithm is such just because shapely does not know how to count the area of ​​geographical objects (at least I did not find how). Therefore, all the same, I decided to leave a binding to postgis, because there it is possible to convert geometries into geography.
Well, then everything is easier: you need to find on the Internet a shape file with the countries polygons, fill it with postgis and make comparisons.
Shapes I took from here . How to load them into the database is written a lot, problems can only be in the encoding, which is not UTF-8.

The simplest part remains: compare squares

 cur.execute("select idx,st_area(geography(st_setsrid(googlegeom,4326)))/1000000, " "st_astext(st_centroid(st_setsrid(googlegeom,4326))) " "from googlePolygons order by idx;") unRealC = cur.fetchall() cur.execute("select name, st_area(geography(st_setsrid(geom,4326)))/1000000, " "st_astext(st_centroid(st_setsrid(geom,4326))) from tm_world_borders;") realC = cur.fetchall() for urc in unRealC: for rc in realC: if float(abs(rc[1]-urc[1])) /rc[1]<0.02: browser.execute_script('pl = new google.maps.Polyline(' '{path: [new google.maps.LatLng('+str(loads(rc[2]).y)+','+ str(loads(rc[2]).x)+'),' ' new google.maps.LatLng('+str(loads(urc[2]).y)+','+ str(loads(urc[2]).x)+')],' 'map: map});') 

I put the float(abs(rc[1]-urc[1])) /rc[1]<0.02 - 2% error. Added a line that connected the center of the original polygon with the center of its likely actual location. loads() - converting WKT to an object.
Here's what happened:

What did not work: Greenland, South Africa, Iceland, Thailand. Using another shape found on the disk, it was possible to achieve with the same 2% that only South Africa and Thailand were undecided.

From the above, it follows that I did not succeed, due to the fact that I do not know from what Google builds the borders of states, and it is also not known how much they have simplified these borders.

I think it would be possible to achieve the result: add a check of the similarity of the polygon shape. But the basic methods of the same Postgis can not do this. The algorithm I think is this: a strong simplification of the geometry, and then a comparison of angles in the allowable range.

Source: https://habr.com/ru/post/169493/


All Articles