/posts  ·   /talks  ·   /boat  ·   /#30DayMapChallenge 2020 | 2022  ·  /About

2022 / Day 19: Globe

Back almost 15-20 years ago when I started working in GIS I remember an older colleague telling me that when choosing coordinate precision in degrees for the area at the latitudes of Estonia the rule of thumb is approximately:

Not exact figures, but more or less.

This is exactly what came to my mind when some time ago the xkcd coordinate precision comic was making its rounds

Coordinate Precision https://t.co/6YMvsNzpDa https://t.co/xZssjYfNGZ pic.twitter.com/UJfTnMuycS

— XKCD Comic (@xkcdComic) July 1, 2019

The funny (at least to me in my mind) thing with a reference system in degrees on a sphere is that the further away you move from the equator towards any of the poles the less precision you would need for your data not to be packed into a “grid”. So for example (but depending on what you’re trying to do - see the xkcd comic above) on the equator it makes a difference if you use 4 decimal places or 5 decimal places for coordinates in degrees. But if you’re exactly at the pole it makes no difference whatsoever - when the latitude is 90° (or -90°) it doesn’t really matter if the precision is zero, one, five, or whatever decimal places. Even more - the meridian value will not have any impact at all. You’ll be -180° .. 180° west and east all the time and at the same time.

Today’s entry to the challenge depicts the relationship between ground distance unit (meter) and the longitude coordinate decimal degree precision as we move from the equator towards any of the poles. Latitude lines of 20 of the worlds biggest cities (according to Wikipedias List of largest cities with their locations from NaturalEarth) are also shown. And in addition Longyearbyen and Ushuaia as a reference. These will serve as the y-axis grid.

The isoprecision lines themselves are calculated in the CTE pts by creating a line from the Null Island to the North pole along the Greenwich meridian with st_makeline and adding vertices to it every 0.1 degrees with st_segmentize and dumping all vertices with st_dumppoints. These will serve as the “original points”.

Next st_translate all the points by delta longitude from a series of precisions in translates. And then create the isoprecision line by creating new nodes at the “original point” st_y but setting the x-coordinate at the value of st_distancespheroid between the “original point” and the corresponding translated point. Finally aggregate these together into a linestring over the used precision value and in the order of original points.

output image

with
    pts as (
        select
            (
                st_dumppoints(
                    st_segmentize(
                        st_makeline(
                            st_point(0,0,4326),
                            st_point(0,90,4326)
                        ),
                        0.1
                    )
                )
            ).*    
    ),
    translates as (
        select
            s, pts.path[1] as ord,
            pts.geom as original, st_translate(pts.geom, s, 0) as geom
        from
            pts,
            unnest(
                array[
                    0.001,
                    0.0001,
                    0.00001,
                    0.000001
                ]
            ) s
    ),
    cities as (
        select
            name, cast(lon as numeric) lon,
            cast(lat as numeric),
            round(cast(lat as numeric),1) as rounded_lat
        from (
            values (
                'Longyearbyen', 15.63, 78.217
            ),(
                'Ushuaia', -68.3, -54.8
            ),(
                'North/South pole', 0.0, 90.0
            ),(
                'Equator', 0.0, 0.0
            ),(
                'Tokyo', 139.749462, 35.686963
            ),(
                'Delhi', 77.199980, 28.600023
            ),(
                'Shanghai', 121.434559, 31.218398
            ),(
                'São Paulo', -46.626966, -23.556734
            ),(
                'Mexico City', -99.132934, 19.444388
            ),(
                'Cairo', 31.248022, 30.051906
            ),(
                'Mumbai', 72.855043, 19.018936
            ),(
                'Beijing', 116.386340, 39.930838
            ),(
                'Dhaka', 90.406634, 23.725006
            ),(
                'Osaka', 135.458199, 34.751981
            ),(
                'New York', -73.981963, 40.751925
            ),(
                'Karachi', 67.01, 24.86
            ),(
                'Buenos Aires', -58.399477, -34.600556
            ),(
                'Chongqing', 106.5504, 29.5637
            ),(
                'Istanbul', 29.008056, 41.106942
            ),(
                'Kolkata',88.37, 22.5675
            ),(
                'Manila',120.980271, 14.606105
            ),(
                'Lagos',3.389585,6.445208
            ),(
                'Rio do Janeiro',-43.226967, -22.923077
            ),(
                'Tianjin', 117.2054, 39.1336
            )
        ) x (name, lon, lat)
    ),
    lines as (
        select
            row_number() over()::int as oid,
            s::varchar,
            st_makeline(
                array_agg(
                    st_point(
                        st_distancespheroid(
                            original,
                            geom,
                            'SPHEROID["WGS 84",6378137,298.257223563]'
                        ),
                        st_y(original)
                    ) order by ord
                )
            ) as geom          
        from translates f
        group by s
    )
select
    row_number() over():: int as id, oid, name, geom, cl
from (
    select
        oid, name,
        case
            when mod(oid,2)=0 then
                st_makeline(
                    st_point(-2.0, abs(c.rounded_lat)),
                    st_point(130.0, abs(c.rounded_lat))
                )
            else
                st_makeline(
                    st_point(-10.0, abs(c.rounded_lat)),
                    st_point(122.0, abs(c.rounded_lat))
                )
        end as geom,
        'cities' as cl
    from (
        select
            row_number() over(order by abs(rounded_lat))::int as oid,
            c.name, c.rounded_lat
        from
           cities c
    ) c
    union all
    select
        oid, s as name, geom, 'isoprecision' as cl
    from
        lines
    union all
    select
        null, m::varchar as name,
        st_makeline(st_point(m,-5), st_point(m,92)),
        'meter-grid' as cl
    from
        generate_series(0, 120, 5) m
) d
;