MapoGPS

7 minute read

Github: https://github.com/lucasoshiro/MapoGPS.

If you are in SĂŁo Paulo, please, enable your location, I promise it will be cool!

So… what is this

Until not long ago, Waze and Google Maps didn’t exit, so people had to use paper maps. The city where I live here in Brazil, São Paulo, is a huge city with more than 12 million people, so, a map of this city is so big that needed to be printed as a book. You can see how it looks here (if you don’t speak Portuguese, it is fine to Google Translate it). We call it “guia de ruas”, that literally means “street guide”.

Personally, when I’m driving I don’t like to hear a voice dictating where I should turn, and I don’t feel comfortable with an app deciding where I should go. I also don’t like how the maps are displayed in those apps, I still prefer how the streets are presented in the street guide. So, even though I’m not so old (I was born in 1996), I keep a street guide in my car (the most famous one, Mapograf, that is still published and updated).

So, I wrote this little utility to convert lat/lng coordinates into the page and coordinates of a street map, for the following reasons:

  • I was bored;
  • I could bring a little bit of technology for something more traditional;

How do we do it?

Street guide coordinate system

The pages that contain the maps are numbered from A1 to A99, from B1 to B27 and from 1 to 432. Each page has a coordinate system where the lines are numbered from 1 to 30 and the columns are identified from A to Z (with some missing letters…).

Conversion overview

The conversion is done in three steps:

  1. Converting latitude and longitude into coordinates in a plane, where the origin is the center of SĂŁo Paulo, and using meters as unit;

  2. Converting the coordinates in that intermediate plane into another plane, where each X unit corresponds to the width of a page, and each Y unit corresponds to the height of a page. The origin of that plane is the crossing between the most western meridian and most northern parallel represented on the street guide;

  3. Converting the plane defined in the step 2 into page numbers and page coordinates;

It resembles a dog, but actually it's the worst map of SĂŁo Paulo you'll see in your life. The red spot is a place and its geographical coordinates.
The same place, but now with its relative position to the city center, after step 1.
Street guide page grid (in blue) overlaying SĂŁo Paulo. In black, the position in (page height, page width) relative to the grid origin, after step 2. In orage, the number of the pages, after step 3.
Location inside the page

However, I’ve written it in the following order:

Step 3

Step 3 is quite simple: in the street guide, before the pages that contain the map itself, there is a small map containing its coverage. That map has a grid that shows what page contains each area of the city. Then, we can see where each page is placed and what pages are its neighbors.

Based on that map, I’ve written the matrix contained in the file pages.py. Note that the matrix pages contains null pages, that represent areas that aren’t covered by any page in the street guide. This pages matrix is used to build a Cartesian plane, where the origin is the top-left corner of the first page of the first row. Actually, the first page of the first row doesn’t physically exists, however, it doesn’t really matter. Each unit of the X axis corresponds to the width of a page, and each unit of the Y axis corresponds to the height of a page.

That way, it is easy to transform a position on the street guide to a position on that Cartesian plane, and also to transform a position in the Cartesian plane to a position in the street guide: Catedral da SĂ© (a church placed on the center of SĂŁo Paulo), can be found in the street guide in 151 Z 3. As the page 151 is placed on the row 13 and on the column 15, we can find the integer part of the position on the Cartesian plane: (13, 15). The non-integer part of the coordinates can be calculated after the coordinates inside the page, Z 3. Then, the position of Catedral da SĂ© on that plane is (13.0834, 15.975).

Step 1

Converting geographical coordinates to meters isn’t straightforward, and in order to do that I needed to make some assumptions.

Assuming that the Earth is a perfect sphere, a degree of latitude has the same length in any place of the planet surface: approximately 10001.965729 km.

The length of a degree of longitude, however, depends on the latitude. The formula for calculating the length in meters of a radian of longitude is given by the following function:

m(lat) = cos(lat) * R

where R is the radius of Earth in meters, and lat is the lattitude in radians.

In order to calculate the average length of a radian of longitude between two points of different lattitudes, it is enough to divide the integral of m between the two lattitudes and divide them by the difference between the lattitudees. Then, we can calculate the average size of a degree, that is:

(Ď€ * R / 180) * (sin(Ď€ * b/180) - sin(Ď€ * a / 180)) / (b - a)

where a and b are the two lattitudes, in degrees.

Once calculated the average size of a degree of longitude, it is enough to multiply it by the difference in longitude in order to get the difference in meters from north to south. And it is enough to multiply the length of a degree of lattitude by the difference in lattitude in order to get the difference in meters from west to east.

Here I need to make a second assumption: that the Earth is flat in the city of SĂŁo Paulo. Just like we did before, that will cause errors, but they are not relevant.

Given that assumption, it is possible to calculate the position of the point relative to the center of SĂŁo Paulo, in meters. Then, we can place it in a Cartesian plane whose origin is the center of SĂŁo Paulo, using meter as unit.

Step 2

In the step 3, we defined a carthesian plane that represents the street guide, and in the step 1 we defined a plane using the metric system. Step 2 consists in converting one plane into another, and it is the main conversion step.

The conversion between the two planes is a linear transformation. However, the transformation matrix is unknown.

In order to find the transformation matrix, I took a sample of several points in the street guide and their geographical coordinates. Those points are in the file pointset.py. The position of those points was calculated accordingly to the definitions described before. Given the positions in both planes, it is possible to perform a linear regression. The linear regression gives us a transformation matrix that minimizes the error when transforming a plane into another. Yay!

Execution

Dependencies

  • Python 3
  • Numpy

How to use

python3 mapo_gps.py

The geographical coordinates should be provided in the decimal notation through stdin, then it will return the corresponding pages and page coordinates.

Found a mistake ?

This script was written for my personal use, and here I’m only sharing how I did it. I’m not a specialist in GIS, in linear algebra, neither in machine learning.

If you find something wrong in the code or even here, I’ll be happy to hear you in a Github issue or Pull Request.

2023 Update

Three years have passed since I wrote that program. A few months after I wrote the first version of it, I began to work on the backend of delivery company. By coincidence, I was part of a team that maintened the internal geocoding microservice! Deal with maps and geographical coordinates, that here was just for fun, soon after became my routine for the next two years!

For whatever reason, in the step 1 I was reinventing the wheel trying to deduce how to measure distances of geographical coordinates. Nowadays I see that lost a lot of time doing that, and I could just use the Haversine formula, that does exactly that.

Between those three years, I wrote an Android app based on that script. As it is only meant for my personal use, I don’t intend to publish on Play Store. It is nothing more than take the geographical coordinates from GPS, apply the transformation described here, and show the results in the screen. Its source code is on my GitHub, if anyone have interest about it:

https://github.com/lucasoshiro/MapoGPSAndroid.

Updated: