Skip to content

Commit

Permalink
Merge 2c92195 into 1c10cb7
Browse files Browse the repository at this point in the history
  • Loading branch information
c0c0n3 committed Oct 5, 2020
2 parents 1c10cb7 + 2c92195 commit 7495aa4
Show file tree
Hide file tree
Showing 27 changed files with 842 additions and 465 deletions.
9 changes: 9 additions & 0 deletions run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ if [ "$tot" -eq 0 ]; then
fi
cd -

cd src/sql/tests
test_suite_header "SQL"
sh run_tests.sh
loc=$?
if [ "$tot" -eq 0 ]; then
tot=$loc
fi
cd -

cd src/utils/tests
test_suite_header "UTILS"
sh run_tests.sh
Expand Down
17 changes: 15 additions & 2 deletions src/geocoding/geojson/wktcodec.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,30 @@
"""

from geomet import wkt, wkb
from typing import Optional


def encode_as_wkt(geo_json: dict, decimals=16) -> str:
def encode_as_wkt(geo_json: dict, decimals: int = 16,
srid: Optional[int] = None) -> str:
"""
Convert the given GeoJSON to WKT.
:param geo_json: the GeoJSON data to convert.
:param decimals: how many decimal digits to use for numbers, defaults to 16.
:param srid: optional spatial reference system ID to include in the shape
metadata. If given, prepend ``SRID=srid;`` to the WKT string. Notice
that SRID isn't part of the WKT spec, but is an additional feature
specified by OpenGIS. Keep this in mind when adding a SRID! Also notice
that if the input GeoJSON already contains a SRID (``meta.srid`` or
``crs.properties.name`` property), that value will be used instead.
:return: the corresponding WKT string.
"""
return wkt.dumps(geo_json, decimals)
wkt_shape = wkt.dumps(geo_json, decimals)
if wkt_shape.lstrip().startswith('SRID'):
return wkt_shape

meta = f"SRID={srid};" if srid is not None else ''
return meta + wkt_shape


def encode_as_wkb(geo_json: dict, big_endian=True) -> bytes:
Expand Down
2 changes: 1 addition & 1 deletion src/geocoding/slf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@


from .geotypes import *
from .jsoncodec import encode
from .jsoncodec import decode, encode
from .locparser import from_location_attribute
from .queryparser import from_geo_params
from .querytypes import *
Expand Down
82 changes: 77 additions & 5 deletions src/geocoding/slf/jsoncodec.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""
This module provides functions to convert a Simple Location Format geometry
to GeoJSON. The only function you're likely to need is the ``encode`` function
which serialises an SLF geometry to a GeoJSON string. The other functions
translate SLF geometries to geometry types from the ``geojson`` library.
to GeoJSON and back. The only functions you're likely to need are the ``encode``
function which serialises an SLF geometry to a GeoJSON string and the ``decode``
function which converts GeoJSON to SLF. The other functions translate SLF
geometries to geometry types from the ``geojson`` library and from GeoJSON
to SLF.
"""

from geojson import dumps, LineString, Point, Polygon
Expand All @@ -26,12 +28,12 @@ def line_to_json_rep(line: SlfLine) -> LineString:

def polygon_to_json_rep(polygon: SlfPolygon) -> Polygon:
ps = list_point_tuples(polygon)
return Polygon(ps)
return Polygon([ps])


def box_to_json_rep(box: SlfBox) -> Polygon:
ps = list_point_tuples(box.to_polygon())
return Polygon(ps)
return Polygon([ps])


def lookup_encoder(geom: SlfGeometry):
Expand Down Expand Up @@ -61,3 +63,73 @@ def encode(geom: SlfGeometry) -> Optional[str]:
if geo_json_rep:
return dumps(geo_json_rep, sort_keys=True)
return None


def geo_type(geo_json: dict) -> Optional[str]:
return maybe_value(geo_json, 'type')


def geo_coords(geo_json: dict) -> Optional[List]:
return maybe_value(geo_json, 'coordinates')


def point_from_geo_coords(xyz: [float]) -> SlfPoint:
return SlfPoint(longitude=xyz[0], latitude=xyz[1])


def geo_point_to_point(geo_json: dict) -> Optional[SlfPoint]:
coords = geo_coords(geo_json)
if geo_type(geo_json) == 'Point' and coords:
return point_from_geo_coords(coords)
return None


def geo_linestring_to_line(geo_json: dict) -> Optional[SlfLine]:
coords = geo_coords(geo_json)
if geo_type(geo_json) == 'LineString' and coords:
ps = [point_from_geo_coords(xyz) for xyz in coords]
return SlfLine(ps)
return None


def geo_polygon_to_polygon(geo_json: dict) -> Optional[SlfPolygon]:
coords = geo_coords(geo_json)
if geo_type(geo_json) == 'Polygon' and coords:
linear_ring = coords[0] # see RFC 7946 § 3.1.6
ps = [point_from_geo_coords(xyz) for xyz in linear_ring]
return SlfPolygon(ps)
return None


def geo_polygon_to_box(geo_json: dict) -> Optional[SlfBox]:
coords = geo_coords(geo_json)
if geo_type(geo_json) == 'Polygon' and coords:
linear_ring = coords[0] # see RFC 7946 § 3.1.6
bottom_right_corner = linear_ring[2] # see SlfBox.to_polygon
top_left_corner = linear_ring[0]
ps = [point_from_geo_coords(bottom_right_corner),
point_from_geo_coords(top_left_corner)]
return SlfBox(ps)
return None


def decode(geo_json: dict, ngsi_type: str) -> Optional[SlfGeometry]:
"""
Convert the given GeoJSON geometry to a Simple Location Format shape.
:param geo_json: the GeoJSON geometry to convert.
:param ngsi_type: the desired output type.
:return: the SLF geometry object corresponding to the input GeoJSON if
its geometry type can be converted to given NGSI SLF type; ``None``
otherwise.
"""
converters = {
SlfPoint.ngsi_type(): geo_point_to_point,
SlfLine.ngsi_type(): geo_linestring_to_line,
SlfPolygon.ngsi_type(): geo_polygon_to_polygon,
SlfBox.ngsi_type(): geo_polygon_to_box
}
converter = converters.get(ngsi_type, None)
if converter:
return converter(geo_json)
return None
56 changes: 44 additions & 12 deletions src/geocoding/slf/tests/test_jsoncodec.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import json
import pytest

from geocoding.slf.geotypes import *
from geocoding.slf.jsoncodec import encode
from geocoding.slf.jsoncodec import decode, encode


def test_none_yields_none():
def test_encode_none_yields_none():
assert encode(None) is None


@pytest.mark.parametrize('ngsi_type', [
SlfPoint.ngsi_type(), SlfLine.ngsi_type(), SlfPolygon.ngsi_type(),
SlfBox.ngsi_type()
])
def test_decode_none_yields_none(ngsi_type):
assert decode(None, ngsi_type) is None


def test_unknown_geom_yields_none():
class UnknownGeom(SlfGeometry):
pass
Expand All @@ -16,51 +26,73 @@ class UnknownGeom(SlfGeometry):

def test_point():
pt = SlfPoint(2, 1)
expected = {
geoj_pt = {
'type': 'Point',
'coordinates': [1, 2]
}

json_str = encode(pt)
assert json_str is not None
assert expected == json.loads(json_str)
assert json.loads(json_str) == geoj_pt

decoded_pt = decode(geoj_pt, SlfPoint.ngsi_type())
assert isinstance(decoded_pt, SlfPoint)
assert decoded_pt.to_ngsi_attribute() == pt.to_ngsi_attribute()


def test_line():
points = [SlfPoint(1, 2), SlfPoint(3, 4)]
line = SlfLine(points)
expected = {
geoj_line = {
'type': 'LineString',
'coordinates': [[2, 1], [4, 3]]
}

json_str = encode(line)
assert json_str is not None
assert expected == json.loads(json_str)
assert json.loads(json_str) == geoj_line

decoded_line = decode(geoj_line, SlfLine.ngsi_type())
assert isinstance(decoded_line, SlfLine)
expected_line = SlfLine(points) # encode consumed pts stream, need new obj
assert decoded_line.to_ngsi_attribute() == expected_line.to_ngsi_attribute()


def test_polygon():
points = [SlfPoint(1, 2), SlfPoint(3, 4), SlfPoint(0, -1), SlfPoint(1, 2)]
polygon = SlfPolygon(points)
expected = {
geoj_polygon = {
'type': 'Polygon',
'coordinates': [[2, 1], [4, 3], [-1, 0], [2, 1]]
'coordinates': [[[2, 1], [4, 3], [-1, 0], [2, 1]]]
}

json_str = encode(polygon)
assert json_str is not None
assert expected == json.loads(json_str)
assert json.loads(json_str) == geoj_polygon

decoded_polygon = decode(geoj_polygon, SlfPolygon.ngsi_type())
assert isinstance(decoded_polygon, SlfPolygon)
# encode consumed pts stream, need new SLF polygon
expected_polygon = SlfPolygon(points)
assert decoded_polygon.to_ngsi_attribute() == \
expected_polygon.to_ngsi_attribute()


def test_box():
brc = SlfPoint(0, 1)
tlc = SlfPoint(1, 0)
box = SlfBox([brc, tlc])
expected = {
geoj_polygon = {
'type': 'Polygon',
'coordinates': [[0, 1], [1, 1], [1, 0], [0, 0], [0, 1]]
'coordinates': [[[0, 1], [1, 1], [1, 0], [0, 0], [0, 1]]]
}

json_str = encode(box)
assert json_str is not None
assert expected == json.loads(json_str)
assert json.loads(json_str) == geoj_polygon

decoded_box = decode(geoj_polygon, SlfBox.ngsi_type())
assert isinstance(decoded_box, SlfBox)
# encode consumed pts stream, need new SLF box
expected_box = SlfBox([brc, tlc])
assert decoded_box.to_ngsi_attribute() == expected_box.to_ngsi_attribute()
12 changes: 10 additions & 2 deletions src/geocoding/slf/wktcodec.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@ def to_wkt_format_string(geom: SlfGeometry) -> Optional[str]:
return None


def encode_as_wkt(geom: SlfGeometry) -> Optional[str]:
def encode_as_wkt(geom: SlfGeometry, srid: Optional[int] = None) \
-> Optional[str]:
"""
Convert the given Simple Location Format shape to the corresponding
WKT shape.
:param geom: the Simple Location Format shape to convert.
:param srid: optional spatial reference system ID to include in the shape
metadata. If given, prepend ``SRID=srid;`` to the WKT string. Notice
that SRID isn't part of the WKT spec, but is an additional feature
specified by OpenGIS. Keep this in mind when adding a SRID!
:return: the WKT string if the input shape is of a known type;
``None`` otherwise.
"""
Expand All @@ -41,7 +46,10 @@ def encode_as_wkt(geom: SlfGeometry) -> Optional[str]:

ps = to_wkt_coords_list(geom._points())
str_rep = to_wkt_format_string(geom)
return str_rep.format(ps) if str_rep else None
if str_rep:
meta = f"SRID={srid};" if srid is not None else ''
return meta + str_rep.format(ps)
return None

# TODO. Use shapely.
# A better option than the above code would be the following which uses the
Expand Down
4 changes: 2 additions & 2 deletions src/geocoding/tests/test_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def test_normalize_get_json_polygon_and_add_or_update_centroid(entity):
'type': 'geo:json',
'value': {
'type': 'Polygon',
'coordinates': [[2, 1], [4, 1], [4, -1], [2, -1]]
'coordinates': [[[2, 1], [4, 1], [4, -1], [2, -1]]]
}
}

Expand All @@ -173,7 +173,7 @@ def test_normalize_get_json_from_box_and_add_or_update_centroid(entity):
'type': 'geo:json',
'value': {
'type': 'Polygon',
'coordinates': [[2, 1], [4, 1], [4, -1], [2, -1], [2, 1]]
'coordinates': [[[2, 1], [4, 1], [4, -1], [2, -1], [2, 1]]]
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/reporter/delete.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from exceptions.exceptions import AmbiguousNGSIIdError
from .http import fiware_s, fiware_sp
from .httputil import fiware_s, fiware_sp
from translators.factory import translator_for


Expand Down
File renamed without changes.
Loading

0 comments on commit 7495aa4

Please sign in to comment.