Skip to content

Commit 77b7974

Browse files
authored
Release 0.5 (#124)
* Fix edit docs link (GH #112) (#114) * Fix bug GH #115 (#117) * Geographical Queries Proposal (#111) * define ql geo-query api. * implement backbone of geo-queries. * document slf package * implement location attr handling. * implement pr suggestions. * use ngsi attr to represent centroid. * implement translation of ngsi geo queries to sql. * hook geo-query code into quantum leap. * close line string when converting box to polygon * use wkt for crate geo queries * missed this file in prev commit :-) * implement end-to-end tests. * enable geo queries independently of geo coding settings. * accept notifs with missing attr values. (#122) closes #110 * optmize docker image (issue #113) (#116) * optmize docker image now 170mb * Avoid pinned pipenv * bump up version number and add 0.5 release notes.
1 parent bfac4d3 commit 77b7974

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+3302
-51
lines changed

Dockerfile

+11-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
FROM python:3.6
2-
RUN pip install 'pipenv==2018.10.13'
3-
1+
FROM python:3.6-alpine as base
2+
FROM base as builder
3+
RUN apk --no-cache --update-cache add gcc gfortran python python-dev py-pip build-base wget freetype-dev libpng-dev openblas-dev
4+
RUN ln -s /usr/include/locale.h /usr/include/xlocale.h
5+
RUN pip install pipenv
46
RUN mkdir -p /src/ngsi-timeseries-api
5-
67
COPY Pipfile /src/ngsi-timeseries-api/Pipfile
78
COPY Pipfile.lock /src/ngsi-timeseries-api/Pipfile.lock
9+
RUN mkdir /install
10+
WORKDIR /install
11+
RUN cd /src/ngsi-timeseries-api && { pipenv lock -r > /requirements.txt; }
12+
RUN pip install --install-option="--prefix=/install" -r /requirements.txt
813

9-
RUN cd /src/ngsi-timeseries-api && { pipenv lock -r > requirements.txt; }
10-
RUN pip install -r /src/ngsi-timeseries-api/requirements.txt
11-
14+
FROM base
15+
COPY --from=builder /install /usr/local
1216
COPY . /src/ngsi-timeseries-api/
13-
1417
WORKDIR /src/ngsi-timeseries-api/src
15-
1618
ENV PYTHONPATH=$PWD:$PYTHONPATH
1719

1820
CMD python app.py

Pipfile

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ connexion = "~=1.1"
88
crate = "~=0.22"
99
flask = "~=0.12"
1010
geocoder = "~=1.33"
11+
geojson = "~=2.4"
1112
influxdb = "~=4.0"
1213
pytest = "~=3.0"
1314
redis = "~=2.10"

Pipfile.lock

+12-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

RELEASE_NOTES.md

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# QuantumLeap Release Notes
22

3+
## 0.5
4+
Release 0.5 of QuantumLeap adds support for geographical queries and features
5+
a streamlined, much smaller docker image as well as several bug fixes.
6+
7+
- Full support for geographical queries as specified by the FIWARE-NGSI v2
8+
Specification except for equality queries (#111)
9+
- Optimised docker image, size is now down to 170 MB (#116)
10+
- Support for missing entity attributes (#122)
11+
- Metadata query fixes (#115)
12+
- Documentation fixes (#112)
13+
314
## 0.4.1
415

516
- Add: /health API endpoint (#68)

mkdocs.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
site_name: QuantumLeap
22
site_url: https://quantumleap.readthedocs.org
33
site_description: A NGSI to TSDB adapter to allow processing of Historical Data.
4-
repo_url: https://github.com/smartsdk/ngsi-timeseries-api.git
5-
edit_uri: 'edit/master/docs/'
4+
repo_url: https://github.com/smartsdk/ngsi-timeseries-api
5+
edit_uri: 'edit/master/docs/manuals'
66
docs_dir: docs/manuals
77
theme: readthedocs
88
markdown_extensions: [toc,fenced_code]

specification/quantumleap.yml

+65
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,38 @@ parameters:
280280
example, if the query was to return 10 results and you use an offset of
281281
1, the response will return the last 9 values. Make sure you don't give
282282
more offset than the number of results."
283+
georel:
284+
in: query
285+
name: georel
286+
type: string
287+
pattern: '^coveredBy$|^intersects$|^equals$|^disjoint$|^near;maxDistance:(0|([1-9][0-9]*))(\.[0-9]+)?$|^near;minDistance:(0|([1-9][0-9]*))(\.[0-9]+)?$|^near;maxDistance:(0|([1-9][0-9]*))(\.[0-9]+)?;minDistance:(0|([1-9][0-9]*))(\.[0-9]+)?$|^near;minDistance:(0|([1-9][0-9]*))(\.[0-9]+)?;maxDistance:(0|([1-9][0-9]*))(\.[0-9]+)?$'
288+
description: "Optional. It specifies a spatial relationship between matching
289+
entities and a reference shape (geometry). This parameter is used to perform
290+
geographical queries with the same semantics as in the FIWARE-NGSI v2 Specification.
291+
Full details can be found in the Geographical Queries section of the specification:
292+
http://fiware.github.io/specifications/ngsiv2/stable/."
293+
geometry:
294+
in: query
295+
name: geometry
296+
type: string
297+
enum: [point, line, polygon, box]
298+
description: "Optional but required if georel is specified. This parameter
299+
defines the reference shape to be used for geographical queries and has the
300+
same semantics as in the FIWARE-NGSI v2 Specification.
301+
Full details can be found in the Geographical Queries section of the specification:
302+
http://fiware.github.io/specifications/ngsiv2/stable/."
303+
coords:
304+
in: query
305+
name: coords
306+
type: string
307+
pattern: '^[+,-]?(0|([1-9][0-9]*))(\.[0-9]+)?,[+,-]?(0|([1-9][0-9]*))(\.[0-9]+)?(;[+,-]?(0|([1-9][0-9]*))(\.[0-9]+)?,[+,-]?(0|([1-9][0-9]*))(\.[0-9]+)?)*$'
308+
description: "Optional but required if georel is specified. This parameter
309+
defines the reference shape (geometry) in terms of WGS 84 coordinates and has
310+
the same semantics as in the FIWARE-NGSI v2 Specification, except we only
311+
accept coordinates in decimal degrees---e.g. `40.714,-74.006` is okay, but not
312+
`40 42' 51'',74 0' 21''`.
313+
Full details can be found in the Geographical Queries section of the specification:
314+
http://fiware.github.io/specifications/ngsiv2/stable/."
283315

284316
################################################################################
285317
# PATHS: META
@@ -612,6 +644,9 @@ paths:
612644
- $ref: '#/parameters/lastN'
613645
- $ref: '#/parameters/limit'
614646
- $ref: '#/parameters/offset'
647+
- $ref: '#/parameters/georel'
648+
- $ref: '#/parameters/geometry'
649+
- $ref: '#/parameters/coords'
615650
# In Header...
616651
- $ref: '#/parameters/fiwareService'
617652
- $ref: '#/parameters/fiwareServicePath'
@@ -683,6 +718,9 @@ paths:
683718
- $ref: '#/parameters/lastN'
684719
- $ref: '#/parameters/limit'
685720
- $ref: '#/parameters/offset'
721+
- $ref: '#/parameters/georel'
722+
- $ref: '#/parameters/geometry'
723+
- $ref: '#/parameters/coords'
686724
# In Header...
687725
- $ref: '#/parameters/fiwareService'
688726
- $ref: '#/parameters/fiwareServicePath'
@@ -741,6 +779,9 @@ paths:
741779
- $ref: '#/parameters/lastN'
742780
- $ref: '#/parameters/limit'
743781
- $ref: '#/parameters/offset'
782+
- $ref: '#/parameters/georel'
783+
- $ref: '#/parameters/geometry'
784+
- $ref: '#/parameters/coords'
744785
# In Header...
745786
- $ref: '#/parameters/fiwareService'
746787
- $ref: '#/parameters/fiwareServicePath'
@@ -857,6 +898,9 @@ paths:
857898
- $ref: '#/parameters/lastN'
858899
- $ref: '#/parameters/limit'
859900
- $ref: '#/parameters/offset'
901+
- $ref: '#/parameters/georel'
902+
- $ref: '#/parameters/geometry'
903+
- $ref: '#/parameters/coords'
860904
# In Header...
861905
- $ref: '#/parameters/fiwareService'
862906
- $ref: '#/parameters/fiwareServicePath'
@@ -936,6 +980,9 @@ paths:
936980
- $ref: '#/parameters/lastN'
937981
- $ref: '#/parameters/limit'
938982
- $ref: '#/parameters/offset'
983+
- $ref: '#/parameters/georel'
984+
- $ref: '#/parameters/geometry'
985+
- $ref: '#/parameters/coords'
939986
# In Header...
940987
- $ref: '#/parameters/fiwareService'
941988
- $ref: '#/parameters/fiwareServicePath'
@@ -1005,6 +1052,9 @@ paths:
10051052
- $ref: '#/parameters/lastN'
10061053
- $ref: '#/parameters/limit'
10071054
- $ref: '#/parameters/offset'
1055+
- $ref: '#/parameters/georel'
1056+
- $ref: '#/parameters/geometry'
1057+
- $ref: '#/parameters/coords'
10081058
# In Header...
10091059
- $ref: '#/parameters/fiwareService'
10101060
- $ref: '#/parameters/fiwareServicePath'
@@ -1068,6 +1118,9 @@ paths:
10681118
- $ref: '#/parameters/lastN'
10691119
- $ref: '#/parameters/limit'
10701120
- $ref: '#/parameters/offset'
1121+
- $ref: '#/parameters/georel'
1122+
- $ref: '#/parameters/geometry'
1123+
- $ref: '#/parameters/coords'
10711124
# In Header...
10721125
- $ref: '#/parameters/fiwareService'
10731126
- $ref: '#/parameters/fiwareServicePath'
@@ -1186,6 +1239,9 @@ paths:
11861239
- $ref: '#/parameters/lastN'
11871240
- $ref: '#/parameters/limit'
11881241
- $ref: '#/parameters/offset'
1242+
- $ref: '#/parameters/georel'
1243+
- $ref: '#/parameters/geometry'
1244+
- $ref: '#/parameters/coords'
11891245
# In Header...
11901246
- $ref: '#/parameters/fiwareService'
11911247
- $ref: '#/parameters/fiwareServicePath'
@@ -1384,6 +1440,9 @@ paths:
13841440
- $ref: '#/parameters/lastN'
13851441
- $ref: '#/parameters/limit'
13861442
- $ref: '#/parameters/offset'
1443+
- $ref: '#/parameters/georel'
1444+
- $ref: '#/parameters/geometry'
1445+
- $ref: '#/parameters/coords'
13871446
# In Header...
13881447
- $ref: '#/parameters/fiwareService'
13891448
- $ref: '#/parameters/fiwareServicePath'
@@ -1483,6 +1542,9 @@ paths:
14831542
- $ref: '#/parameters/lastN'
14841543
- $ref: '#/parameters/limit'
14851544
- $ref: '#/parameters/offset'
1545+
- $ref: '#/parameters/georel'
1546+
- $ref: '#/parameters/geometry'
1547+
- $ref: '#/parameters/coords'
14861548
# In Header...
14871549
- $ref: '#/parameters/fiwareService'
14881550
- $ref: '#/parameters/fiwareServicePath'
@@ -1593,6 +1655,9 @@ paths:
15931655
- $ref: '#/parameters/lastN'
15941656
- $ref: '#/parameters/limit'
15951657
- $ref: '#/parameters/offset'
1658+
- $ref: '#/parameters/georel'
1659+
- $ref: '#/parameters/geometry'
1660+
- $ref: '#/parameters/coords'
15961661
# In Header...
15971662
- $ref: '#/parameters/fiwareService'
15981663
- $ref: '#/parameters/fiwareServicePath'

src/geocoding/centroid.py

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from numbers import Real
2+
from typing import Sequence
3+
from geojson.utils import coords
4+
5+
6+
def centroid2d(points):
7+
"""
8+
Calculate the centroid of a finite set of 2D points.
9+
10+
:param points: the set of points as an iterable. Each point is assumed to
11+
be a sequence of (at least) two real numbers ``[x, y]``, respectively
12+
the x (first element) and y coordinate (second element).
13+
:return: the centroid or ``None`` if the input is empty.
14+
:raise IndexError: if any point is an empty list.
15+
:raise TypeError: if the input is ``None`` or a point has a ``None`` coord
16+
or the coord isn't a number.
17+
"""
18+
number_of_points = 0
19+
centroid = [0, 0]
20+
21+
for p in points:
22+
centroid = (centroid[0] + p[0], centroid[1] + p[1])
23+
number_of_points += 1
24+
25+
centroid = [centroid[0]/number_of_points, centroid[1]/number_of_points] \
26+
if number_of_points else None
27+
28+
return centroid
29+
#
30+
# NOTE. Performance.
31+
# This function can compute the centroid of a few thousands points in
32+
# microseconds but runtime goes up to millis on huge input lists---over
33+
# 100,000 points.
34+
# Even though we could use NumPy to bring the runtime back to microseconds on
35+
# huge lists (see e.g. https://stackoverflow.com/questions/23020659), the time
36+
# it takes to convert the input to a NumPy array is about 10 times what this
37+
# function actually takes to compute the result.
38+
#
39+
# NOTE. Typing.
40+
# Should we use complex numbers instead of lists?
41+
#
42+
43+
44+
def is_point(coords_list):
45+
return coords_list and \
46+
isinstance(coords_list, Sequence) and \
47+
len(coords_list) > 1 and \
48+
isinstance(coords_list[0], Real) and \
49+
isinstance(coords_list[1], Real)
50+
51+
52+
def best_effort_centroid2d(points):
53+
"""
54+
Calculate the centroid of the finite set of 2D points found in the input.
55+
A 2D point is assumed to be a list of at least two numbers, the first
56+
being the x coordinate and the second the y. This function first filters
57+
any non-2D point element out of the input and then computes the centroid
58+
of the remaining set of 2D points if such set isn't empty. If the filtered
59+
set is empty, then ``None`` is returned.
60+
61+
:param points: the set of points as an iterable.
62+
:return: the centroid or ``None`` if it couldn't be computed.
63+
"""
64+
ps = filter(is_point, points if points else [])
65+
return centroid2d(ps)
66+
67+
68+
def maybe_centroid2d(points):
69+
"""
70+
Same as ``centroid2d`` but returns ``None`` instead of raising an exception
71+
if the input isn't in the expected format.
72+
"""
73+
try:
74+
return centroid2d(points)
75+
except (ZeroDivisionError, TypeError, IndexError):
76+
return None
77+
78+
79+
def geojson_centroid(obj):
80+
"""
81+
Compute the centroid of the set of points that define the shapes in the
82+
input GeoJSON object.
83+
84+
:param obj: a GeoJSON object.
85+
:return: the centroid if it could be calculated or ``None`` if there was
86+
an error---e.g. the input GeoJSON contains invalid points.
87+
"""
88+
points = coords(obj)
89+
return best_effort_centroid2d(points)

0 commit comments

Comments
 (0)