π Interoperability & Conversions

Obtaining coordinates from markers is quite straightforward β analogously to map.getCenter()
, you'd call marker.getPosition()
. Since that'd return a LatLng
object instance, you'd need to chain .toJSON()
to arrive at the familiar lat,lng
coordinate pair:
const position = { lat: 40.7419, lng: -73.9921 };
const marker = new google.maps.Marker({
map,
position,
});
const extractedPosition = marker.getPosition().getCenter();
// returns {lat: 40.7419, lng: -73.9921}
Equally straightforward is obtaining a circle
's center β you'd need circle.getCenter().toJSON()
.
But what about more complicated shapes like polylines and markers? How do you obtain their coordinates?
- A polyline's coordinates form a path.
- Calling
polygon.getPath()
returns an MVCArray. - Calling
polygon.getPath().getArray()
returns an iterable list ofLatLng
objects. - Finally, calling
.toJSON()
on each object individually produces a familiar list of coordinates.
const nycToLaFlightCoords = [
{ lat: 40.7419, lng: -73.9921 },
{ lat: 37.772, lng: -122.214 },
];
const flightPolyline = new google.maps.Polyline({
map,
path: nycToLaFlightCoords,
geodesic: true,
});
const coords = flightPolyline
.getPath()
.getArray()
.map((latLng) => latLng.toJSON());
// returns [{"lat":40.7419,"lng":-73.9921},{"lat":37.772,"lng":-122.214}]
A polygon's coordinates form a closed loop. Similarly to polylines, you'll need to call .getPath()
and .getArray()
on the polygon object:
const polygonCoords = [
{ lat: 40.7419, lng: -73.9921 },
{ lat: 37.772, lng: -122.214 },
{ lat: 32.321, lng: -64.757 },
{ lat: 40.7419, lng: -73.9921 },
];
const polygon = new google.maps.Polygon({
map,
paths: polygonCoords,
});
const coords = polygon
.getPath()
.getArray()
.map((latLng) => latLng.toJSON());
// returns [{"lat":40.7419,"lng":-73.9921},{"lat":37.772,"lng":-122.214},{"lat":32.321,"lng":-64.757},{"lat":40.7419,"lng":-73.9921}]
A multipolygon's coordinates wrap polygon loops within a βparentβ array. In pseudo code:
multiPolyCoords = [
coordsOfPoly_1,
coordsOfPoly_2,
...
];
// concretely
const multiPolygonCoords = [
[
{ lat: 40.7419, lng: -73.9921 },
{ lat: 37.772, lng: -122.214 },
{ lat: 32.321, lng: -64.757 },
{ lat: 40.7419, lng: -73.9921 }
],
[
{ lat: 40.7419, lng: -73.9921 },
{ lat: 37.772, lng: -122.214 },
{ lat: 32.321, lng: -64.757 },
{ lat: 40.7419, lng: -73.9921 }
]
];
If you want to retain the individual polygon separation, use:
const multiPoly = new google.maps.Polygon({
map,
paths: multiPolygonCoords,
});
const coords = multiPoly
.getPaths()
.getArray()
.map((arr) => arr.getArray().map((latLng) => latLng.toJSON()));
// returns [[{"lat":40.7419,"lng":-73.9921},{"lat":37.772,"lng":-122.214},{"lat":32.321,"lng":-64.757},{"lat":40.7419,"lng":-73.9921}],[{"lat":40.7419,"lng":-73.9921},{"lat":37.772,"lng":-122.214},{"lat":32.321,"lng":-64.757},{"lat":40.7419,"lng":-73.9921}]]
Alternatively, to flatten the coordinates, use:
const coords = triangle
.getPaths()
.getArray()
.flatMap((arr) => arr.getArray())
.map((latLng) => latLng.toJSON());
// returns [{"lat":40.7419,"lng":-73.9921},{"lat":37.772,"lng":-122.214},{"lat":32.321,"lng":-64.757},{"lat":40.7419,"lng":-73.9921},{"lat":40.7419,"lng":-73.9921},{"lat":37.772,"lng":-122.214},{"lat":32.321,"lng":-64.757},{"lat":40.7419,"lng":-73.9921}]
Speaking of coordinates, there is a multitude of geodetic coordinate reference systems out there, the most common of which is WGS84
β used by GPS, Google Maps, and others. Virtually all these systems are standardized by an organization called EPSG and so each reference system has its own EPSG notation or code β WGS84
corresponds to EPSG:4326
and so on.
When you encounter coordinates other than EPSG:4326
, the data provider will likely include the EPSG code somewhere within the dataset (usually in the properties
field of a GeoJSON file, or in the metadata of other file types.)
For instance, these coordinates in meters β [360590, 555610]
β were specified as EPSG:27700
. So, if we now know the origin and the target coordinate system (:27700 β :4326), we can leverage the conversion utility proj4
to transform the meter coordinates into latitudes & longitudes.
Proj4
has bindings in many popular programming languages including JavaScript
:
import proj4 from "proj4";
// from https://epsg.io/27700
const EPSG_27700_DATUM =
"+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs";
// from https://epsg.io/4326
const WGS84_DATUM = "+proj=longlat +datum=WGS84 +no_defs";
// [x -> lon, y -> lat ]
const point = [360590, 555610];
// yielding [-2.61597, 54.89366] -> [lon, lat]
const converted_point = proj4(EPSG_27700_DATUM, WGS84_DATUM, point);
Transforming meter coordinates into lat/long using proj4js
As a zoomable canvas, your map restricts the world to a rectangular viewport. This viewport is bounded on each side by its cardinal direction β west , south , east , north :
North (+90)
|
|
(-180) West ---+--- East (+180)
|
|
South (-90)
Now, to describe the viewport with the smallest number of points, we'll need the rectangle's opposing corners β typically the south west and the north east:
- South West β Bottom Left β [ min(x) , min(y) ]
- North East β Top Right β [ max(x) , max(y) ]
The opposing corners' coordinates can also be deconstructed into a bounding box:
[ min(x), min(y), max(x), max(y) ] or
[ sw_lng, sw_lat, ne_lng, ne_lat ] or
[ **left, bottom, right, top** ] or
[ west, south, east, north ]
This is super useful for when you're passing the viewport from the map to some backend service. You see, lots of geospatial databases (such as Elasticsearch
and BigQuery
) expect the viewport to be formatted as a bounding box.