togo 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of togo might be problematic. Click here for more details.
- togo-0.1.0/LICENSE +21 -0
- togo-0.1.0/MANIFEST.in +5 -0
- togo-0.1.0/PKG-INFO +141 -0
- togo-0.1.0/README.md +126 -0
- togo-0.1.0/pyproject.toml +12 -0
- togo-0.1.0/setup.cfg +4 -0
- togo-0.1.0/setup.py +47 -0
- togo-0.1.0/tests/test_factories.py +60 -0
- togo-0.1.0/tests/test_geometries.py +60 -0
- togo-0.1.0/tests/test_geometry.py +158 -0
- togo-0.1.0/tests/test_line.py +50 -0
- togo-0.1.0/tests/test_point.py +30 -0
- togo-0.1.0/tests/test_poly.py +61 -0
- togo-0.1.0/tests/test_rect.py +64 -0
- togo-0.1.0/tests/test_ring.py +66 -0
- togo-0.1.0/tg.c +15948 -0
- togo-0.1.0/tg.h +359 -0
- togo-0.1.0/togo.c +27981 -0
- togo-0.1.0/togo.egg-info/PKG-INFO +141 -0
- togo-0.1.0/togo.egg-info/SOURCES.txt +21 -0
- togo-0.1.0/togo.egg-info/dependency_links.txt +1 -0
- togo-0.1.0/togo.egg-info/top_level.txt +1 -0
- togo-0.1.0/togo.pyx +810 -0
togo-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Giorgio Salluzzo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
togo-0.1.0/MANIFEST.in
ADDED
togo-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: togo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight Python bindings for the TG geometry library
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Dynamic: description
|
|
10
|
+
Dynamic: description-content-type
|
|
11
|
+
Dynamic: license
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
Dynamic: requires-python
|
|
14
|
+
Dynamic: summary
|
|
15
|
+
|
|
16
|
+
# ToGo
|
|
17
|
+
ToGo - Python bindings for TG
|
|
18
|
+
(Geometry library for C - Fast point-in-polygon)
|
|
19
|
+
|
|
20
|
+
# How ToGo Works
|
|
21
|
+
|
|
22
|
+
Togo is a high-performance Python library for computational geometry, providing a Cython wrapper around the [tg](https://github.com/tidwall/tg) C library.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install togo
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- Fast and efficient geometric operations
|
|
33
|
+
- Support for standard geometry types: Point, Line, Ring, Polygon, and their multi-variants
|
|
34
|
+
- Geometric predicates: contains, intersects, covers, touches, etc.
|
|
35
|
+
- Format conversion between WKT, GeoJSON, WKB, and HEX
|
|
36
|
+
- Spatial indexing for accelerated queries
|
|
37
|
+
- Memory-efficient C implementation with Python-friendly interface
|
|
38
|
+
|
|
39
|
+
## Basic Usage
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from togo import Geometry, Point, Ring, Poly
|
|
43
|
+
|
|
44
|
+
# Create a geometry from GeoJSON
|
|
45
|
+
geom = Geometry('{"type":"Point","coordinates":[1.0,2.0]}')
|
|
46
|
+
|
|
47
|
+
# Create a point
|
|
48
|
+
point = Point(1.0, 2.0)
|
|
49
|
+
|
|
50
|
+
# Create a polygon
|
|
51
|
+
ring = Ring([(0,0), (10,0), (10,10), (0,10), (0,0)])
|
|
52
|
+
polygon = Poly(ring)
|
|
53
|
+
|
|
54
|
+
# Convert to various formats
|
|
55
|
+
wkt = polygon.as_geometry().to_wkt()
|
|
56
|
+
geojson = polygon.as_geometry().to_geojson()
|
|
57
|
+
|
|
58
|
+
# Perform spatial predicates
|
|
59
|
+
point_geom = point.as_geometry()
|
|
60
|
+
contains = polygon.as_geometry().contains(point_geom)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Core Classes
|
|
64
|
+
|
|
65
|
+
### Geometry
|
|
66
|
+
|
|
67
|
+
The base class that wraps tg_geom structures and provides core operations:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
# Create from various formats
|
|
71
|
+
g1 = Geometry('POINT(1 2)', fmt='wkt')
|
|
72
|
+
g2 = Geometry('{"type":"Point","coordinates":[1,2]}', fmt='geojson')
|
|
73
|
+
|
|
74
|
+
# Geometric predicates
|
|
75
|
+
g1.intersects(g2)
|
|
76
|
+
g1.contains(g2)
|
|
77
|
+
g1.within(g2)
|
|
78
|
+
|
|
79
|
+
# Format conversion
|
|
80
|
+
g1.to_wkt()
|
|
81
|
+
g1.to_geojson()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Point, Line, Ring, Poly
|
|
85
|
+
|
|
86
|
+
Building blocks for constructing geometries:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
# Create a point
|
|
90
|
+
p = Point(1.0, 2.0)
|
|
91
|
+
|
|
92
|
+
# Create a line
|
|
93
|
+
line = Line([(0,0), (1,1), (2,2)])
|
|
94
|
+
|
|
95
|
+
# Create a ring (closed line)
|
|
96
|
+
ring = Ring([(0,0), (10,0), (10,10), (0,10), (0,0)])
|
|
97
|
+
|
|
98
|
+
# Create a polygon with holes
|
|
99
|
+
exterior = Ring([(0,0), (10,0), (10,10), (0,10), (0,0)])
|
|
100
|
+
hole = Ring([(2,2), (8,2), (8,8), (2,8), (2,2)])
|
|
101
|
+
poly = Poly(exterior, [hole])
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### MultiGeometries
|
|
105
|
+
|
|
106
|
+
Creating collections of geometries:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
# Create a MultiPoint
|
|
110
|
+
multi_point = Geometry.from_multipoint([(0,0), (1,1), Point(2,2)])
|
|
111
|
+
|
|
112
|
+
# Create a MultiLineString
|
|
113
|
+
multi_line = Geometry.from_multilinestring([
|
|
114
|
+
[(0,0), (1,1)],
|
|
115
|
+
Line([(2,2), (3,3)])
|
|
116
|
+
])
|
|
117
|
+
|
|
118
|
+
# Create a MultiPolygon
|
|
119
|
+
poly1 = Poly(Ring([(0,0), (1,0), (1,1), (0,1), (0,0)]))
|
|
120
|
+
poly2 = Poly(Ring([(2,2), (3,2), (3,3), (2,3), (2,2)]))
|
|
121
|
+
multi_poly = Geometry.from_multipolygon([poly1, poly2])
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Polygon Indexing
|
|
125
|
+
|
|
126
|
+
Togo supports different polygon indexing strategies for optimized spatial operations:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from togo import TGIndex, set_polygon_indexing_mode
|
|
130
|
+
|
|
131
|
+
# Set the indexing mode
|
|
132
|
+
set_polygon_indexing_mode(TGIndex.NATURAL) # or NONE, YSTRIPES
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Performance Considerations
|
|
136
|
+
|
|
137
|
+
- Togo is optimized for speed and memory efficiency
|
|
138
|
+
- For large datasets, proper indexing can significantly improve performance
|
|
139
|
+
- Creating geometries with the appropriate format avoids unnecessary conversions
|
|
140
|
+
|
|
141
|
+
Soon there will be a full API documentation, for now please refer to the test suite for more usage examples.
|
togo-0.1.0/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# ToGo
|
|
2
|
+
ToGo - Python bindings for TG
|
|
3
|
+
(Geometry library for C - Fast point-in-polygon)
|
|
4
|
+
|
|
5
|
+
# How ToGo Works
|
|
6
|
+
|
|
7
|
+
Togo is a high-performance Python library for computational geometry, providing a Cython wrapper around the [tg](https://github.com/tidwall/tg) C library.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install togo
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- Fast and efficient geometric operations
|
|
18
|
+
- Support for standard geometry types: Point, Line, Ring, Polygon, and their multi-variants
|
|
19
|
+
- Geometric predicates: contains, intersects, covers, touches, etc.
|
|
20
|
+
- Format conversion between WKT, GeoJSON, WKB, and HEX
|
|
21
|
+
- Spatial indexing for accelerated queries
|
|
22
|
+
- Memory-efficient C implementation with Python-friendly interface
|
|
23
|
+
|
|
24
|
+
## Basic Usage
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from togo import Geometry, Point, Ring, Poly
|
|
28
|
+
|
|
29
|
+
# Create a geometry from GeoJSON
|
|
30
|
+
geom = Geometry('{"type":"Point","coordinates":[1.0,2.0]}')
|
|
31
|
+
|
|
32
|
+
# Create a point
|
|
33
|
+
point = Point(1.0, 2.0)
|
|
34
|
+
|
|
35
|
+
# Create a polygon
|
|
36
|
+
ring = Ring([(0,0), (10,0), (10,10), (0,10), (0,0)])
|
|
37
|
+
polygon = Poly(ring)
|
|
38
|
+
|
|
39
|
+
# Convert to various formats
|
|
40
|
+
wkt = polygon.as_geometry().to_wkt()
|
|
41
|
+
geojson = polygon.as_geometry().to_geojson()
|
|
42
|
+
|
|
43
|
+
# Perform spatial predicates
|
|
44
|
+
point_geom = point.as_geometry()
|
|
45
|
+
contains = polygon.as_geometry().contains(point_geom)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Core Classes
|
|
49
|
+
|
|
50
|
+
### Geometry
|
|
51
|
+
|
|
52
|
+
The base class that wraps tg_geom structures and provides core operations:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
# Create from various formats
|
|
56
|
+
g1 = Geometry('POINT(1 2)', fmt='wkt')
|
|
57
|
+
g2 = Geometry('{"type":"Point","coordinates":[1,2]}', fmt='geojson')
|
|
58
|
+
|
|
59
|
+
# Geometric predicates
|
|
60
|
+
g1.intersects(g2)
|
|
61
|
+
g1.contains(g2)
|
|
62
|
+
g1.within(g2)
|
|
63
|
+
|
|
64
|
+
# Format conversion
|
|
65
|
+
g1.to_wkt()
|
|
66
|
+
g1.to_geojson()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Point, Line, Ring, Poly
|
|
70
|
+
|
|
71
|
+
Building blocks for constructing geometries:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
# Create a point
|
|
75
|
+
p = Point(1.0, 2.0)
|
|
76
|
+
|
|
77
|
+
# Create a line
|
|
78
|
+
line = Line([(0,0), (1,1), (2,2)])
|
|
79
|
+
|
|
80
|
+
# Create a ring (closed line)
|
|
81
|
+
ring = Ring([(0,0), (10,0), (10,10), (0,10), (0,0)])
|
|
82
|
+
|
|
83
|
+
# Create a polygon with holes
|
|
84
|
+
exterior = Ring([(0,0), (10,0), (10,10), (0,10), (0,0)])
|
|
85
|
+
hole = Ring([(2,2), (8,2), (8,8), (2,8), (2,2)])
|
|
86
|
+
poly = Poly(exterior, [hole])
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### MultiGeometries
|
|
90
|
+
|
|
91
|
+
Creating collections of geometries:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
# Create a MultiPoint
|
|
95
|
+
multi_point = Geometry.from_multipoint([(0,0), (1,1), Point(2,2)])
|
|
96
|
+
|
|
97
|
+
# Create a MultiLineString
|
|
98
|
+
multi_line = Geometry.from_multilinestring([
|
|
99
|
+
[(0,0), (1,1)],
|
|
100
|
+
Line([(2,2), (3,3)])
|
|
101
|
+
])
|
|
102
|
+
|
|
103
|
+
# Create a MultiPolygon
|
|
104
|
+
poly1 = Poly(Ring([(0,0), (1,0), (1,1), (0,1), (0,0)]))
|
|
105
|
+
poly2 = Poly(Ring([(2,2), (3,2), (3,3), (2,3), (2,2)]))
|
|
106
|
+
multi_poly = Geometry.from_multipolygon([poly1, poly2])
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Polygon Indexing
|
|
110
|
+
|
|
111
|
+
Togo supports different polygon indexing strategies for optimized spatial operations:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from togo import TGIndex, set_polygon_indexing_mode
|
|
115
|
+
|
|
116
|
+
# Set the indexing mode
|
|
117
|
+
set_polygon_indexing_mode(TGIndex.NATURAL) # or NONE, YSTRIPES
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Performance Considerations
|
|
121
|
+
|
|
122
|
+
- Togo is optimized for speed and memory efficiency
|
|
123
|
+
- For large datasets, proper indexing can significantly improve performance
|
|
124
|
+
- Creating geometries with the appropriate format avoids unnecessary conversions
|
|
125
|
+
|
|
126
|
+
Soon there will be a full API documentation, for now please refer to the test suite for more usage examples.
|
togo-0.1.0/setup.cfg
ADDED
togo-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import urllib.request
|
|
3
|
+
from urllib.parse import urlparse
|
|
4
|
+
|
|
5
|
+
from setuptools import setup, Extension
|
|
6
|
+
from Cython.Build import cythonize
|
|
7
|
+
|
|
8
|
+
TG_SOURCE_URL = "https://raw.githubusercontent.com/tidwall/tg/main/tg.c"
|
|
9
|
+
TG_SOURCE_FILENAME = os.path.basename(urlparse(TG_SOURCE_URL).path)
|
|
10
|
+
|
|
11
|
+
if not os.path.exists(TG_SOURCE_FILENAME):
|
|
12
|
+
urllib.request.urlretrieve(TG_SOURCE_URL, TG_SOURCE_FILENAME)
|
|
13
|
+
|
|
14
|
+
TG_HEADER_URL = "https://raw.githubusercontent.com/tidwall/tg/main/tg.h"
|
|
15
|
+
TG_HEADER_FILENAME = os.path.basename(urlparse(TG_HEADER_URL).path)
|
|
16
|
+
|
|
17
|
+
if not os.path.exists(TG_HEADER_FILENAME):
|
|
18
|
+
urllib.request.urlretrieve(TG_HEADER_URL, TG_HEADER_FILENAME)
|
|
19
|
+
|
|
20
|
+
# Package metadata
|
|
21
|
+
NAME = "togo"
|
|
22
|
+
VERSION = "0.1.0"
|
|
23
|
+
DESCRIPTION = "Lightweight Python bindings for the TG geometry library"
|
|
24
|
+
LONG_DESCRIPTION = ""
|
|
25
|
+
LONG_DESCRIPTION_CONTENT_TYPE = "text/markdown"
|
|
26
|
+
if os.path.exists("README.md"):
|
|
27
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
28
|
+
LONG_DESCRIPTION = f.read()
|
|
29
|
+
|
|
30
|
+
setup(
|
|
31
|
+
name=NAME,
|
|
32
|
+
version=VERSION,
|
|
33
|
+
description=DESCRIPTION,
|
|
34
|
+
long_description=LONG_DESCRIPTION,
|
|
35
|
+
long_description_content_type=LONG_DESCRIPTION_CONTENT_TYPE,
|
|
36
|
+
license="MIT",
|
|
37
|
+
ext_modules=cythonize(
|
|
38
|
+
[
|
|
39
|
+
Extension(
|
|
40
|
+
"togo",
|
|
41
|
+
sources=["togo.pyx", TG_SOURCE_FILENAME],
|
|
42
|
+
include_dirs=["."],
|
|
43
|
+
)
|
|
44
|
+
]
|
|
45
|
+
),
|
|
46
|
+
python_requires=">=3.8",
|
|
47
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from togo import Geometry, Point, Line, Ring, Poly
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_multipoint_from_tuples():
|
|
5
|
+
pts = [(0, 0), (1, 2), (2, -1)]
|
|
6
|
+
mp1 = Geometry.from_multipoint(pts)
|
|
7
|
+
assert mp1.type_string() == "MultiPoint"
|
|
8
|
+
assert mp1.rect() == ((0.0, -1.0), (2.0, 2.0))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_multipoint_from_points():
|
|
12
|
+
pts_obj = [Point(3, 3), Point(4, 5)]
|
|
13
|
+
mp2 = Geometry.from_multipoint(pts_obj)
|
|
14
|
+
assert mp2.type_string() == "MultiPoint"
|
|
15
|
+
assert mp2.rect() == ((3.0, 3.0), (4.0, 5.0))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_multilinestring_from_coords():
|
|
19
|
+
ml1 = Geometry.from_multilinestring(
|
|
20
|
+
[
|
|
21
|
+
[(0, 0), (1, 1)],
|
|
22
|
+
[(2, 2), (3, 3), (4, 3)],
|
|
23
|
+
]
|
|
24
|
+
)
|
|
25
|
+
assert ml1.type_string() == "MultiLineString"
|
|
26
|
+
assert ml1.num_lines() == 2
|
|
27
|
+
assert ml1.rect() == ((0.0, 0.0), (4.0, 3.0))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_multilinestring_from_lines():
|
|
31
|
+
l1 = Line([(10, 10), (11, 12)])
|
|
32
|
+
l2 = Line([(9, 8), (10, 7)])
|
|
33
|
+
ml2 = Geometry.from_multilinestring([l1, l2])
|
|
34
|
+
assert ml2.type_string() == "MultiLineString"
|
|
35
|
+
assert ml2.num_lines() == 2
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_multipolygon_from_polys():
|
|
39
|
+
outer1 = Ring([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)])
|
|
40
|
+
poly1 = Poly(outer1)
|
|
41
|
+
outer2 = Ring([(2, 2), (3, 2), (3, 3), (2, 3), (2, 2)])
|
|
42
|
+
poly2 = Poly(outer2)
|
|
43
|
+
mp = Geometry.from_multipolygon([poly1, poly2])
|
|
44
|
+
assert mp.type_string() == "MultiPolygon"
|
|
45
|
+
assert mp.num_polys() == 2
|
|
46
|
+
assert mp.rect() == ((0.0, 0.0), (3.0, 3.0))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_geometrycollection_mixed_inputs():
|
|
50
|
+
point = Point(1, 2)
|
|
51
|
+
line = Line([(0, 0), (1, 1)])
|
|
52
|
+
ring = Ring([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)])
|
|
53
|
+
poly = Poly(ring)
|
|
54
|
+
geom_point = point.as_geometry()
|
|
55
|
+
|
|
56
|
+
gc = Geometry.from_geometrycollection(
|
|
57
|
+
[geom_point, line, ring, poly, (5, 6), Point(7, 8)]
|
|
58
|
+
)
|
|
59
|
+
assert gc.type_string() == "GeometryCollection"
|
|
60
|
+
assert gc.num_geometries() == 6
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from togo import Geometry, Rect, Point
|
|
2
|
+
|
|
3
|
+
from tests.geometries import TOGO, BENIN, GRAN_BUENOS_AIRES_AREA
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_togo():
|
|
7
|
+
togo = Geometry(TOGO)
|
|
8
|
+
assert togo.memsize() == 6080
|
|
9
|
+
assert not togo.is_empty()
|
|
10
|
+
assert not togo.has_z()
|
|
11
|
+
assert togo.dims() == 2
|
|
12
|
+
assert togo.type_string() == "Polygon"
|
|
13
|
+
bbox = togo.rect()
|
|
14
|
+
assert bbox == ((-0.149762, 6.100546), (1.799327, 11.13854))
|
|
15
|
+
min_, max_ = Point(*bbox[0]), Point(*bbox[1])
|
|
16
|
+
rect = Rect(min_, max_)
|
|
17
|
+
assert rect.min.as_tuple() == bbox[0]
|
|
18
|
+
assert rect.max.as_tuple() == bbox[1]
|
|
19
|
+
togo_center = rect.center()
|
|
20
|
+
assert togo_center.as_tuple() == (0.8247825, 8.619543)
|
|
21
|
+
assert togo.intersects(togo_center.as_geometry())
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_benin():
|
|
25
|
+
benin = Geometry(BENIN, fmt="geojson")
|
|
26
|
+
assert benin.memsize() == 6688
|
|
27
|
+
assert not benin.is_empty()
|
|
28
|
+
assert not benin.has_z()
|
|
29
|
+
assert benin.dims() == 2
|
|
30
|
+
assert benin.type_string() == "Polygon"
|
|
31
|
+
bbox = benin.rect()
|
|
32
|
+
assert bbox == ((0.776667, 6.218721), (3.855, 12.396658))
|
|
33
|
+
min_, max_ = Point(*bbox[0]), Point(*bbox[1])
|
|
34
|
+
rect = Rect(min_, max_)
|
|
35
|
+
assert rect.min.as_tuple() == bbox[0]
|
|
36
|
+
assert rect.max.as_tuple() == bbox[1]
|
|
37
|
+
benin_center = rect.center()
|
|
38
|
+
assert benin_center.as_tuple() == (2.3158335, 9.3076895)
|
|
39
|
+
assert benin.intersects(benin_center.as_geometry())
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_buenos_aires():
|
|
43
|
+
b_aires = Geometry(GRAN_BUENOS_AIRES_AREA, fmt="wkt")
|
|
44
|
+
assert b_aires.memsize() == 456
|
|
45
|
+
assert not b_aires.is_empty()
|
|
46
|
+
assert b_aires.has_z()
|
|
47
|
+
assert b_aires.dims() == 3
|
|
48
|
+
assert b_aires.type_string() == "Polygon"
|
|
49
|
+
bbox = b_aires.rect()
|
|
50
|
+
assert bbox == (
|
|
51
|
+
(-59.01433270595447, -35.07421568123671),
|
|
52
|
+
(-57.75103350378777, -34.33189712265184),
|
|
53
|
+
)
|
|
54
|
+
min_, max_ = Point(*bbox[0]), Point(*bbox[1])
|
|
55
|
+
rect = Rect(min_, max_)
|
|
56
|
+
assert rect.min.as_tuple() == bbox[0]
|
|
57
|
+
assert rect.max.as_tuple() == bbox[1]
|
|
58
|
+
b_aires_center = rect.center()
|
|
59
|
+
assert b_aires_center.as_tuple() == (-58.38268310487112, -34.70305640194427)
|
|
60
|
+
assert b_aires.intersects(b_aires_center.as_geometry())
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from togo import Geometry
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_geometry_wkt():
|
|
6
|
+
g = Geometry("POINT(1 2)", fmt="wkt")
|
|
7
|
+
assert g.type_string() == "Point"
|
|
8
|
+
assert g.rect() == ((1.0, 2.0), (1.0, 2.0))
|
|
9
|
+
assert g.memsize() == 24
|
|
10
|
+
assert g.num_points() == 0 # FIXME? Points in a Point geometry is considered 0
|
|
11
|
+
assert not g.is_feature()
|
|
12
|
+
assert not g.is_featurecollection()
|
|
13
|
+
assert not g.is_empty()
|
|
14
|
+
assert g.dims() == 2
|
|
15
|
+
assert g.has_z() is False
|
|
16
|
+
assert g.has_m() is False
|
|
17
|
+
assert g.to_wkt() == "POINT(1 2)"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_geometry_equals():
|
|
21
|
+
g1 = Geometry("POINT(1 2)", fmt="wkt")
|
|
22
|
+
g2 = Geometry("POINT(1 2)", fmt="wkt")
|
|
23
|
+
g3 = Geometry("POINT(2 3)", fmt="wkt")
|
|
24
|
+
assert g1.equals(g2)
|
|
25
|
+
assert not g1.equals(g3)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_geometry_disjoint():
|
|
29
|
+
g1 = Geometry("POINT(1 2)", fmt="wkt")
|
|
30
|
+
g2 = Geometry("POINT(2 3)", fmt="wkt")
|
|
31
|
+
assert g1.disjoint(g2)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_geometry_contains():
|
|
35
|
+
g1 = Geometry("POINT(1 2)", fmt="wkt")
|
|
36
|
+
g2 = Geometry("POINT(1 2)", fmt="wkt")
|
|
37
|
+
assert g1.contains(g2)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_geometry_to_geojson():
|
|
41
|
+
g = Geometry("POINT(1 2)", fmt="wkt")
|
|
42
|
+
geojson = g.to_geojson()
|
|
43
|
+
assert geojson == '{"type":"Point","coordinates":[1,2]}'
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_geometry_to_hex():
|
|
47
|
+
g = Geometry("POINT(1 2)", fmt="wkt")
|
|
48
|
+
hexstr = g.to_hex()
|
|
49
|
+
assert hexstr == "0101000000000000000000F03F0000000000000040"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_geometry_to_wkb():
|
|
53
|
+
g = Geometry("POINT(1 2)", fmt="wkt")
|
|
54
|
+
wkb = g.to_wkb()
|
|
55
|
+
assert (
|
|
56
|
+
wkb
|
|
57
|
+
== b"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_geometry_to_geobin():
|
|
62
|
+
g = Geometry("POINT(1 2)", fmt="wkt")
|
|
63
|
+
geobin = g.to_geobin()
|
|
64
|
+
assert (
|
|
65
|
+
geobin
|
|
66
|
+
== b"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_geometry_intersects():
|
|
71
|
+
g1 = Geometry("POLYGON((10 10,20 10,20 20,10 20,10 10))", fmt="wkt")
|
|
72
|
+
g2 = Geometry("POINT(15 15)", fmt="wkt")
|
|
73
|
+
g3 = Geometry("POINT(23 24)", fmt="wkt")
|
|
74
|
+
assert g1.intersects(g2)
|
|
75
|
+
assert not g1.intersects(g3)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_equals():
|
|
79
|
+
g1 = Geometry("POINT(1 2)", fmt="wkt")
|
|
80
|
+
g2 = Geometry('{"type":"Point","coordinates":[1,2]}', fmt="geojson")
|
|
81
|
+
g3 = Geometry("POINT(2 3)", fmt="wkt")
|
|
82
|
+
assert g1.equals(g2)
|
|
83
|
+
assert not g1.equals(g3)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_geometry_num_lines_polys_geometries():
|
|
87
|
+
g_lines = Geometry("MULTILINESTRING((0 0,1 1),(2 2,3 3))", fmt="wkt")
|
|
88
|
+
assert g_lines.num_lines() == 2
|
|
89
|
+
g_polys = Geometry(
|
|
90
|
+
"MULTIPOLYGON(((0 0,1 0,1 1,0 1,0 0)),((2 2,3 2,3 3,2 3,2 2)))", fmt="wkt"
|
|
91
|
+
)
|
|
92
|
+
assert g_polys.num_polys() == 2
|
|
93
|
+
g_collection = Geometry(
|
|
94
|
+
"GEOMETRYCOLLECTION(POINT(1 2),LINESTRING(0 0,1 1))", fmt="wkt"
|
|
95
|
+
)
|
|
96
|
+
assert g_collection.num_geometries() == 2
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_geometry_z_m():
|
|
100
|
+
g = Geometry("POINT ZM (1 2 3 4)", fmt="wkt")
|
|
101
|
+
assert g.has_z() is True
|
|
102
|
+
assert g.has_m() is True
|
|
103
|
+
assert g.z() == 3.0
|
|
104
|
+
assert g.m() == 4.0
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_geometry_spatial_predicates():
|
|
108
|
+
g1 = Geometry("POLYGON((0 0,2 0,2 2,0 2,0 0))", fmt="wkt")
|
|
109
|
+
g2 = Geometry("POINT(1 1)", fmt="wkt")
|
|
110
|
+
g3 = Geometry("POINT(3 3)", fmt="wkt")
|
|
111
|
+
assert g1.covers(g2)
|
|
112
|
+
assert g2.coveredby(g1)
|
|
113
|
+
assert not g1.covers(g3)
|
|
114
|
+
assert not g3.coveredby(g1)
|
|
115
|
+
assert g1.touches(Geometry("LINESTRING(0 0,2 0)", fmt="wkt"))
|
|
116
|
+
assert g1.intersects(g2)
|
|
117
|
+
assert not g1.intersects(g3)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_geometry_to_wkb_and_geobin():
|
|
121
|
+
g = Geometry("POINT(1 2)", fmt="wkt")
|
|
122
|
+
wkb = g.to_wkb()
|
|
123
|
+
geobin = g.to_geobin()
|
|
124
|
+
assert isinstance(wkb, bytes)
|
|
125
|
+
assert isinstance(geobin, bytes)
|
|
126
|
+
assert len(wkb) > 0
|
|
127
|
+
assert len(geobin) > 0
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_geometry_constructor_wkt():
|
|
131
|
+
g = Geometry("POINT(1 2)", fmt="wkt")
|
|
132
|
+
assert g.type_string() == "Point"
|
|
133
|
+
assert g.rect() == ((1.0, 2.0), (1.0, 2.0))
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_geometry_constructor_geojson():
|
|
137
|
+
geojson = '{"type":"Point","coordinates":[1,2]}'
|
|
138
|
+
g = Geometry(geojson, fmt="geojson")
|
|
139
|
+
assert g.type_string() == "Point"
|
|
140
|
+
assert g.rect() == ((1.0, 2.0), (1.0, 2.0))
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_geometry_constructor_hex():
|
|
144
|
+
hexstr = "0101000000000000000000F03F0000000000000040"
|
|
145
|
+
g = Geometry(hexstr, fmt="hex")
|
|
146
|
+
assert g.type_string() == "Point"
|
|
147
|
+
assert g.rect() == ((1.0, 2.0), (1.0, 2.0))
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_geometry_constructor_invalid():
|
|
151
|
+
# Note: The Geometry constructor only supports 'wkt', 'geojson', and 'hex' formats.
|
|
152
|
+
# 'wkb' is not a supported input format for the constructor.
|
|
153
|
+
with pytest.raises(ValueError):
|
|
154
|
+
Geometry("not a geometry", fmt="wkt")
|
|
155
|
+
with pytest.raises(ValueError):
|
|
156
|
+
Geometry("not a geometry", fmt="geojson")
|
|
157
|
+
with pytest.raises(ValueError):
|
|
158
|
+
Geometry("not a geometry", fmt="hex")
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from togo import Line, Rect, Geometry
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_line_basic():
|
|
6
|
+
points = [(0, 0), (1, 1), (2, 2)]
|
|
7
|
+
line = Line(points)
|
|
8
|
+
assert line.num_points() == 3
|
|
9
|
+
assert line.points() == points
|
|
10
|
+
assert isinstance(line.length(), float)
|
|
11
|
+
rect = line.rect()
|
|
12
|
+
assert isinstance(rect, Rect)
|
|
13
|
+
assert rect.min.as_tuple() == (0, 0)
|
|
14
|
+
assert rect.max.as_tuple() == (2, 2)
|
|
15
|
+
assert line.is_clockwise() in (True, False)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_line_two_points():
|
|
19
|
+
points = [(0, 0), (1, 0)]
|
|
20
|
+
line = Line(points)
|
|
21
|
+
assert line.num_points() == 2
|
|
22
|
+
assert line.points() == points
|
|
23
|
+
assert line.length() == pytest.approx(1.0)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_line_collinear():
|
|
27
|
+
points = [(0, 0), (1, 1), (2, 2)]
|
|
28
|
+
line = Line(points)
|
|
29
|
+
# Length should be sqrt(2) + sqrt(2)
|
|
30
|
+
expected_length = 2**0.5 + 2**0.5
|
|
31
|
+
assert line.length() == pytest.approx(expected_length)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_line_closed():
|
|
35
|
+
points = [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)]
|
|
36
|
+
line = Line(points)
|
|
37
|
+
assert line.num_points() == 5
|
|
38
|
+
assert line.points() == points
|
|
39
|
+
# Perimeter of a square
|
|
40
|
+
assert line.length() == pytest.approx(4.0)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_line_as_geometry():
|
|
44
|
+
points = [(0, 0), (1, 1), (2, 2)]
|
|
45
|
+
line = Line(points)
|
|
46
|
+
g = line.as_geometry()
|
|
47
|
+
assert isinstance(g, Geometry)
|
|
48
|
+
assert g.type_string() == "LineString"
|
|
49
|
+
rect = g.rect()
|
|
50
|
+
assert rect == ((0.0, 0.0), (2.0, 2.0))
|