Skip to content

Commit

Permalink
support to workaround antimeridian problems
Browse files Browse the repository at this point in the history
  • Loading branch information
xrotwang committed Jun 18, 2024
1 parent 96e16d0 commit 8c7292b
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changes

## Unreleased

Added function to translate GeoJSON objects to be "pacific centered".


## [0.4.0] - 2024-06-11

- Enhanced `fixed_geometry`, adding option to fix antimeridian issues.
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,26 @@ pip install cldfgeojson
```


## Adding speaker areas to CLDF datasets
## Creating CLDF datasets with speaker area data in GeoJSON

The functionality in [`cldfgeojson.create`](src/cldfgeojson/create.py) helps adding speaker area
information when creating CLDF datasets (e.g. with [`cldfbench`](https://github.com/cldf/cldfbench)).


## Manipulating geo-referenced images in GeoTIFF format

The [`cldfgeojson.geotiff`](src/cldfgeojson/geotiff.py) module provides functionality related to
images in [GeoTIFF](https://en.wikipedia.org/wiki/GeoTIFF) format.


## Commandline interface

`cldfgeojson` also provides [`cldfbench` sub-commands](https://github.com/cldf/cldfbench?tab=readme-ov-file#commands):

- [`geojson.webmercator`](src/cldfgeojson/commands/webmercator.py)
- [`geojson.overlay`](src/cldfgeojson/commands/overlay.py)


## `leaflet.draw`

This package contains the [`leaflet.draw`](https://github.com/Leaflet/Leaflet.draw) plugin in the form of `data://` URLs in
Expand Down
2 changes: 2 additions & 0 deletions src/cldfgeojson/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ def aggregate(shapes: typing.Iterable[typing.Tuple[str, geojson.Feature, str]],
typing.List[geojson.Feature],
typing.List[typing.Tuple[Languoid, list, str]]]:
"""
Aggregate features, merging based on same language of family level Glottocode.
:param shapes: Iterable of (feature ID, GeoJSON feature, Glottocode) triples.
:param glottolog: Glottolog data can be supplied either as `pyglottolog.Glottolog` API object \
or as glottolog-cldf `pycldf.Dataset`.
Expand Down
49 changes: 48 additions & 1 deletion src/cldfgeojson/geojson.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import typing

__all__ = ['MEDIA_TYPE', 'Geometry', 'Feature']
__all__ = ['MEDIA_TYPE', 'Geometry', 'Feature', 'pacific_centered']

# See https://datatracker.ietf.org/doc/html/rfc7946#section-12
MEDIA_TYPE = 'application/geo+json'
Expand Down Expand Up @@ -33,3 +33,50 @@ class Feature(typing.TypedDict, total=False):
properties: typing.Union[None, JSONObject]
id: typing.Union[str, float, int]
bbox: list


def pacific_centered(obj: typing.Union[Geometry, Feature]) -> typing.Union[Geometry, Feature]:
"""
Adjust longitudes of coordinates of objects to force a pacific-centered position in place.
Some mapping tools do not support "continuous worlds", i.e. displaying the same content at
360° longitude increments. A typical workaround is "moving" objects to the appropriate "world
copy". The same workaround also works for tools like shapely, which do maths in the cartesian
plane, to avoid problems caused by the antimeridian.
For language data, it is often useful to display pacific-centered maps, in order to not cut
through the area of any language family. For this purpose, 154°E is a suitable central
longitude, because the cut at 26°W does not cut through any macroareas. See
https://en.wikipedia.org/wiki/154th_meridian_east and
https://en.wikipedia.org/wiki/26th_meridian_west
:return:
"""
PACIFIC_CENTERED = 154

def fix_position(pos):
pos = list(pos)
if pos[0] <= PACIFIC_CENTERED - 180:
# Anything west of 26°W is moved by 360°.
pos[0] += 360
return pos

geom = obj['geometry'] if 'geometry' in obj else obj
if geom['type'] == 'Point':
# Point -> [lon, lat]
geom['coordinates'] = fix_position(geom['coordinates'])
elif geom['type'] in {'MultiPoint', 'LineString'}:
# MultiPoint -> [[lon, lat]..]
# LineString -> [[lon, lat]..]
geom['coordinates'] = [fix_position(pos) for pos in geom['coordinates']]
elif geom['type'] in {'Polygon', 'MultiLineString'}:
# Polygon -> [[[lon, lat]..]..]
# MultiLineString -> [[[lon, lat]..]..]
geom['coordinates'] = [[fix_position(pos) for pos in line] for line in geom['coordinates']]
else:
# MultiPolygon -> [[[[lon, lat]..]..]..]
assert geom['type'] == 'MultiPolygon'
geom['coordinates'] = [[[
fix_position(pos) for pos in line] for line in poly] for poly in geom['coordinates']]

return obj
19 changes: 19 additions & 0 deletions tests/test_geojson.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest
from shapely.geometry import shape

from cldfgeojson.geojson import *


@pytest.mark.parametrize(
'type_,coords,check',
[
('Point', [-170, 0], lambda s: s.x > 0),
('MultiPoint', [[-170, 0], [170, 0]], lambda s: s.centroid.x == 180),
('Polygon', [[[170, 0], [-170, 1], [-170, -1], [170, 0]]], lambda s: s.centroid.x > 180),
('MultiPolygon',
[[[[170, 0], [-170, 1], [-170, -1], [170, 0]]]],
lambda s: s.centroid.x > 180)
]
)
def test_pacific_centered(type_, coords, check):
assert check(shape(pacific_centered(dict(type=type_, coordinates=coords))))

0 comments on commit 8c7292b

Please sign in to comment.