geotypes 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.
- geotypes-0.1.0/LICENSE.txt +21 -0
- geotypes-0.1.0/PKG-INFO +208 -0
- geotypes-0.1.0/README.md +161 -0
- geotypes-0.1.0/pyproject.toml +73 -0
- geotypes-0.1.0/setup.cfg +4 -0
- geotypes-0.1.0/src/geotypes/__init__.py +43 -0
- geotypes-0.1.0/src/geotypes/features.py +164 -0
- geotypes-0.1.0/src/geotypes/geometry.py +1650 -0
- geotypes-0.1.0/src/geotypes/rasters.py +603 -0
- geotypes-0.1.0/src/geotypes.egg-info/PKG-INFO +208 -0
- geotypes-0.1.0/src/geotypes.egg-info/SOURCES.txt +12 -0
- geotypes-0.1.0/src/geotypes.egg-info/dependency_links.txt +1 -0
- geotypes-0.1.0/src/geotypes.egg-info/requires.txt +20 -0
- geotypes-0.1.0/src/geotypes.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jackson Shields
|
|
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.
|
geotypes-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: geotypes
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A set of useful utilities for working with geospatial data. Makes it simpler to do common operations.
|
|
5
|
+
Author-email: Jackson Shields <jacksonhshields@gmail.com>
|
|
6
|
+
Maintainer-email: Jackson Shields <jacksonhshields@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/jacksonhshields/geotypes
|
|
9
|
+
Project-URL: Documentation, https://jacksonhshields.github.io/geotypes/
|
|
10
|
+
Project-URL: Repository, https://github.com/jacksonhshields/geotypes.git
|
|
11
|
+
Project-URL: Issues, https://github.com/jacksonhshields/geotypes/issues
|
|
12
|
+
Keywords: geospatial,gis,geopandas,rasterio,shapely,coordinates,utm,navigation
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
25
|
+
Requires-Python: >=3.8
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE.txt
|
|
28
|
+
Requires-Dist: pandas
|
|
29
|
+
Requires-Dist: numpy
|
|
30
|
+
Requires-Dist: matplotlib
|
|
31
|
+
Requires-Dist: geopandas
|
|
32
|
+
Requires-Dist: affine
|
|
33
|
+
Requires-Dist: rasterio
|
|
34
|
+
Requires-Dist: pymap3d
|
|
35
|
+
Requires-Dist: utm
|
|
36
|
+
Requires-Dist: geopy
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest; extra == "dev"
|
|
39
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
40
|
+
Requires-Dist: black; extra == "dev"
|
|
41
|
+
Requires-Dist: isort; extra == "dev"
|
|
42
|
+
Requires-Dist: flake8; extra == "dev"
|
|
43
|
+
Provides-Extra: docs
|
|
44
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
45
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
|
|
48
|
+
# geotypes
|
|
49
|
+
|
|
50
|
+
geotypes is a python package for making it easy to work with geospatial data, particularly useful for geospatial planning and analysis. It wraps GeoPandas and rasterio with simple data types to make it easier and more pythonic to work with for some applications.
|
|
51
|
+
|
|
52
|
+
Visit the [documentation](https://jacksonhshields.github.io/geotypes/) for installation, getting started, tutorials and API reference.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
Geotypes adds some high-level wrappers around geopandas and shapely to allow for easier use. They allow easy conversion between georeference schemes, better tracking of what coordinate system the variables are in and easy conversion between types.
|
|
57
|
+
These include:
|
|
58
|
+
|
|
59
|
+
* GeoPoint
|
|
60
|
+
* LL
|
|
61
|
+
* GeoPath
|
|
62
|
+
* GeoPoints
|
|
63
|
+
* GeoArea
|
|
64
|
+
* XY
|
|
65
|
+
* LocalPath
|
|
66
|
+
* LocalPoints
|
|
67
|
+
* LocalArea
|
|
68
|
+
* Raster
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
Use pip to install geotypes.
|
|
74
|
+
|
|
75
|
+
GDAL is not added to the requirements. If you already have libgdal-dev installed, you can use:
|
|
76
|
+
```
|
|
77
|
+
gdalversion=$(gdalinfo --version | cut -d\ -f2 | sed s'#,##') && pip3 install GDAL==$gdalversion
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
With GDAL installed or not, you can install geotypes with:
|
|
83
|
+
```bash
|
|
84
|
+
pip install git@github.com:jacksonhshields/geotypes.git
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
## Tutorials
|
|
90
|
+
|
|
91
|
+
To get started with geotypes, check out the tutorials, which run you through the data types and how to use them.
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
## Basic Usage
|
|
95
|
+
|
|
96
|
+
### GeoPoint
|
|
97
|
+
The GeoPoint is an x,y coordinate with an associated Coordinate Reference System (CRS). It allows easy conversion to LL and XY data types.
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
p = geotypes.geometry.GeoPoint(x=151.28826000, y=-33.79798000, crs="epsg:4326") # initialise a lat, lon centred in Manly.
|
|
101
|
+
print("GeoPoint", p)
|
|
102
|
+
ll = p.to_ll() # Converts to lat,lon data type
|
|
103
|
+
print("In LL", ll)
|
|
104
|
+
p_utm = p.to_crs("EPSG:32756") # converts to utm zone 56S
|
|
105
|
+
print("In UTM Zone 56S", p_utm)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### LL
|
|
109
|
+
|
|
110
|
+
## LL
|
|
111
|
+
The lat,lon data type is there for convience and to make it explicit about what is latitude, what is longitude.
|
|
112
|
+
```
|
|
113
|
+
ll = geotypes.geometry.LL(-33.79798000,151.28826000)
|
|
114
|
+
print(ll)
|
|
115
|
+
# Convert to geopoint
|
|
116
|
+
p = ll.to_geopoint()
|
|
117
|
+
print(p)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
### GeoPath
|
|
122
|
+
A path in geographic space, effectively a geopandas geoseries / shapely linestring with an associated CRS. It is commonly used to represent sampling transects (such as AUV transects). It has some convient functions to collect samples at set distances along the path - which can be done no-matter what coordinate system the path is in.
|
|
123
|
+
```
|
|
124
|
+
# Define a set of points. This is a simple broad grid in CRS "EPSG:32756"
|
|
125
|
+
points = [(342041.99358729343, 6259392.849573873),
|
|
126
|
+
(342495.27623430215, 6259603.771833958),
|
|
127
|
+
(342579.64503957686, 6259422.458960008),
|
|
128
|
+
(342126.36269032373, 6259211.536449324),
|
|
129
|
+
(342210.73189359176, 6259030.223443877),
|
|
130
|
+
(342664.01394509675, 6259241.146205157)]
|
|
131
|
+
|
|
132
|
+
# create the geopath object
|
|
133
|
+
geopath = geotypes.geometry.GeoPath(points, crs="EPSG:32756")
|
|
134
|
+
print(geopath)
|
|
135
|
+
|
|
136
|
+
# Iterating over it returns a geopoint.
|
|
137
|
+
print("Geopoints:")
|
|
138
|
+
for p in geopath:
|
|
139
|
+
print(p)
|
|
140
|
+
print("\n")
|
|
141
|
+
|
|
142
|
+
# Easily convert to other crs
|
|
143
|
+
geopath4326 = geopath.to_crs("EPSG:4326")
|
|
144
|
+
print(geopath4326)
|
|
145
|
+
|
|
146
|
+
# Sample points along the geopath
|
|
147
|
+
geopath_subsampled = geopath.sample_points_along_path(d=20) # sample a point every 20 meters.
|
|
148
|
+
print("Number of points in original {}, in subsampled {}".format(len(geopath), len(geopath_subsampled)))
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
### GeoArea
|
|
153
|
+
|
|
154
|
+
A GeoArea is a wrapper around a geopandas GeoSeries with just one element, a polygon or multipolygon. They are used within the situ framework to represent sampling bounds or focal areas. As such, they have easy access to random sampling.
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
# Define a geoarea
|
|
158
|
+
geoarea = geotypes.geometry.GeoArea([(151.28412747953516, -33.78781691040293),
|
|
159
|
+
(151.30572252046485, -33.78781691040293),
|
|
160
|
+
(151.30572478467255, -33.805848131225765),
|
|
161
|
+
(151.28412521532746, -33.805848131225765),
|
|
162
|
+
(151.28412747953516, -33.78781691040293)], crs='EPSG:4326')
|
|
163
|
+
|
|
164
|
+
# get some samples from within it - these are GeoPoints:
|
|
165
|
+
for n in range(5):
|
|
166
|
+
print(geoarea.random_point_geo())
|
|
167
|
+
# you can get also get points directly as LLs, regardless of the CRS.
|
|
168
|
+
for n in range(5):
|
|
169
|
+
print(geoarea.random_point_ll())
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Local Equivalents
|
|
173
|
+
There are local equivalents to the classes presented above. These include:
|
|
174
|
+
- XY: Equivalent of GeoPoint
|
|
175
|
+
- LocalPoints: Equivalent of GeoPoints
|
|
176
|
+
- LocalPath: Equivalent of GeoPath
|
|
177
|
+
- LocalArea: Equivalent of GeoArea
|
|
178
|
+
These are just wrappers around the shapely geometry classes, but offer some extra functionality, including being able to convert to and from the Geo equivalents.
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
origin_ll = geoarea.centroid_ll()
|
|
182
|
+
# Convert the position to local
|
|
183
|
+
xy = p.to_local(origin_ll)
|
|
184
|
+
# Now go back to GeoPoint, this time with a different CRS.
|
|
185
|
+
pll = xy.to_geo(origin_ll, "epsg:4326")
|
|
186
|
+
# Print all to compare
|
|
187
|
+
print(p, xy, pll)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# Convert a GeoPath to a LocalPath
|
|
191
|
+
localpath = geopath.to_local(origin_ll)
|
|
192
|
+
# And go back again
|
|
193
|
+
geopath2 = localpath.to_geo(origin_ll, geopath.crs)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Rasters
|
|
197
|
+
|
|
198
|
+
There is an abstraction of rasterio Rasters to make it easier to work with rasters. You can use the Raster.get_value function with any geotypes geometry object to get the value or values at that point.
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
from geotypes.rasters import Raster
|
|
202
|
+
r = Raster('manly_uw.tif')
|
|
203
|
+
ll = LL(-33.79931, 151.29412)
|
|
204
|
+
from geotypes.geometry import LL
|
|
205
|
+
ll = LL(-33.79931, 151.29412)
|
|
206
|
+
r.get_value(ll)
|
|
207
|
+
>>> -7.871370315551758
|
|
208
|
+
```
|
geotypes-0.1.0/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# geotypes
|
|
2
|
+
|
|
3
|
+
geotypes is a python package for making it easy to work with geospatial data, particularly useful for geospatial planning and analysis. It wraps GeoPandas and rasterio with simple data types to make it easier and more pythonic to work with for some applications.
|
|
4
|
+
|
|
5
|
+
Visit the [documentation](https://jacksonhshields.github.io/geotypes/) for installation, getting started, tutorials and API reference.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Geotypes adds some high-level wrappers around geopandas and shapely to allow for easier use. They allow easy conversion between georeference schemes, better tracking of what coordinate system the variables are in and easy conversion between types.
|
|
10
|
+
These include:
|
|
11
|
+
|
|
12
|
+
* GeoPoint
|
|
13
|
+
* LL
|
|
14
|
+
* GeoPath
|
|
15
|
+
* GeoPoints
|
|
16
|
+
* GeoArea
|
|
17
|
+
* XY
|
|
18
|
+
* LocalPath
|
|
19
|
+
* LocalPoints
|
|
20
|
+
* LocalArea
|
|
21
|
+
* Raster
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
Use pip to install geotypes.
|
|
27
|
+
|
|
28
|
+
GDAL is not added to the requirements. If you already have libgdal-dev installed, you can use:
|
|
29
|
+
```
|
|
30
|
+
gdalversion=$(gdalinfo --version | cut -d\ -f2 | sed s'#,##') && pip3 install GDAL==$gdalversion
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
With GDAL installed or not, you can install geotypes with:
|
|
36
|
+
```bash
|
|
37
|
+
pip install git@github.com:jacksonhshields/geotypes.git
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## Tutorials
|
|
43
|
+
|
|
44
|
+
To get started with geotypes, check out the tutorials, which run you through the data types and how to use them.
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## Basic Usage
|
|
48
|
+
|
|
49
|
+
### GeoPoint
|
|
50
|
+
The GeoPoint is an x,y coordinate with an associated Coordinate Reference System (CRS). It allows easy conversion to LL and XY data types.
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
p = geotypes.geometry.GeoPoint(x=151.28826000, y=-33.79798000, crs="epsg:4326") # initialise a lat, lon centred in Manly.
|
|
54
|
+
print("GeoPoint", p)
|
|
55
|
+
ll = p.to_ll() # Converts to lat,lon data type
|
|
56
|
+
print("In LL", ll)
|
|
57
|
+
p_utm = p.to_crs("EPSG:32756") # converts to utm zone 56S
|
|
58
|
+
print("In UTM Zone 56S", p_utm)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### LL
|
|
62
|
+
|
|
63
|
+
## LL
|
|
64
|
+
The lat,lon data type is there for convience and to make it explicit about what is latitude, what is longitude.
|
|
65
|
+
```
|
|
66
|
+
ll = geotypes.geometry.LL(-33.79798000,151.28826000)
|
|
67
|
+
print(ll)
|
|
68
|
+
# Convert to geopoint
|
|
69
|
+
p = ll.to_geopoint()
|
|
70
|
+
print(p)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
### GeoPath
|
|
75
|
+
A path in geographic space, effectively a geopandas geoseries / shapely linestring with an associated CRS. It is commonly used to represent sampling transects (such as AUV transects). It has some convient functions to collect samples at set distances along the path - which can be done no-matter what coordinate system the path is in.
|
|
76
|
+
```
|
|
77
|
+
# Define a set of points. This is a simple broad grid in CRS "EPSG:32756"
|
|
78
|
+
points = [(342041.99358729343, 6259392.849573873),
|
|
79
|
+
(342495.27623430215, 6259603.771833958),
|
|
80
|
+
(342579.64503957686, 6259422.458960008),
|
|
81
|
+
(342126.36269032373, 6259211.536449324),
|
|
82
|
+
(342210.73189359176, 6259030.223443877),
|
|
83
|
+
(342664.01394509675, 6259241.146205157)]
|
|
84
|
+
|
|
85
|
+
# create the geopath object
|
|
86
|
+
geopath = geotypes.geometry.GeoPath(points, crs="EPSG:32756")
|
|
87
|
+
print(geopath)
|
|
88
|
+
|
|
89
|
+
# Iterating over it returns a geopoint.
|
|
90
|
+
print("Geopoints:")
|
|
91
|
+
for p in geopath:
|
|
92
|
+
print(p)
|
|
93
|
+
print("\n")
|
|
94
|
+
|
|
95
|
+
# Easily convert to other crs
|
|
96
|
+
geopath4326 = geopath.to_crs("EPSG:4326")
|
|
97
|
+
print(geopath4326)
|
|
98
|
+
|
|
99
|
+
# Sample points along the geopath
|
|
100
|
+
geopath_subsampled = geopath.sample_points_along_path(d=20) # sample a point every 20 meters.
|
|
101
|
+
print("Number of points in original {}, in subsampled {}".format(len(geopath), len(geopath_subsampled)))
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
### GeoArea
|
|
106
|
+
|
|
107
|
+
A GeoArea is a wrapper around a geopandas GeoSeries with just one element, a polygon or multipolygon. They are used within the situ framework to represent sampling bounds or focal areas. As such, they have easy access to random sampling.
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
# Define a geoarea
|
|
111
|
+
geoarea = geotypes.geometry.GeoArea([(151.28412747953516, -33.78781691040293),
|
|
112
|
+
(151.30572252046485, -33.78781691040293),
|
|
113
|
+
(151.30572478467255, -33.805848131225765),
|
|
114
|
+
(151.28412521532746, -33.805848131225765),
|
|
115
|
+
(151.28412747953516, -33.78781691040293)], crs='EPSG:4326')
|
|
116
|
+
|
|
117
|
+
# get some samples from within it - these are GeoPoints:
|
|
118
|
+
for n in range(5):
|
|
119
|
+
print(geoarea.random_point_geo())
|
|
120
|
+
# you can get also get points directly as LLs, regardless of the CRS.
|
|
121
|
+
for n in range(5):
|
|
122
|
+
print(geoarea.random_point_ll())
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Local Equivalents
|
|
126
|
+
There are local equivalents to the classes presented above. These include:
|
|
127
|
+
- XY: Equivalent of GeoPoint
|
|
128
|
+
- LocalPoints: Equivalent of GeoPoints
|
|
129
|
+
- LocalPath: Equivalent of GeoPath
|
|
130
|
+
- LocalArea: Equivalent of GeoArea
|
|
131
|
+
These are just wrappers around the shapely geometry classes, but offer some extra functionality, including being able to convert to and from the Geo equivalents.
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
origin_ll = geoarea.centroid_ll()
|
|
135
|
+
# Convert the position to local
|
|
136
|
+
xy = p.to_local(origin_ll)
|
|
137
|
+
# Now go back to GeoPoint, this time with a different CRS.
|
|
138
|
+
pll = xy.to_geo(origin_ll, "epsg:4326")
|
|
139
|
+
# Print all to compare
|
|
140
|
+
print(p, xy, pll)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# Convert a GeoPath to a LocalPath
|
|
144
|
+
localpath = geopath.to_local(origin_ll)
|
|
145
|
+
# And go back again
|
|
146
|
+
geopath2 = localpath.to_geo(origin_ll, geopath.crs)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Rasters
|
|
150
|
+
|
|
151
|
+
There is an abstraction of rasterio Rasters to make it easier to work with rasters. You can use the Raster.get_value function with any geotypes geometry object to get the value or values at that point.
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
from geotypes.rasters import Raster
|
|
155
|
+
r = Raster('manly_uw.tif')
|
|
156
|
+
ll = LL(-33.79931, 151.29412)
|
|
157
|
+
from geotypes.geometry import LL
|
|
158
|
+
ll = LL(-33.79931, 151.29412)
|
|
159
|
+
r.get_value(ll)
|
|
160
|
+
>>> -7.871370315551758
|
|
161
|
+
```
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "geotypes"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A set of useful utilities for working with geospatial data. Makes it simpler to do common operations."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Jackson Shields", email = "jacksonhshields@gmail.com"}
|
|
13
|
+
]
|
|
14
|
+
maintainers = [
|
|
15
|
+
{name = "Jackson Shields", email = "jacksonhshields@gmail.com"}
|
|
16
|
+
]
|
|
17
|
+
keywords = ["geospatial", "gis", "geopandas", "rasterio", "shapely", "coordinates", "utm", "navigation"]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 3 - Alpha",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Intended Audience :: Science/Research",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.8",
|
|
26
|
+
"Programming Language :: Python :: 3.9",
|
|
27
|
+
"Programming Language :: Python :: 3.10",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Topic :: Scientific/Engineering :: GIS",
|
|
31
|
+
]
|
|
32
|
+
requires-python = ">=3.8"
|
|
33
|
+
dependencies = [
|
|
34
|
+
"pandas",
|
|
35
|
+
"numpy",
|
|
36
|
+
"matplotlib",
|
|
37
|
+
"geopandas",
|
|
38
|
+
"affine",
|
|
39
|
+
"rasterio",
|
|
40
|
+
"pymap3d",
|
|
41
|
+
"utm",
|
|
42
|
+
"geopy",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.optional-dependencies]
|
|
46
|
+
dev = [
|
|
47
|
+
"pytest",
|
|
48
|
+
"pytest-cov",
|
|
49
|
+
"black",
|
|
50
|
+
"isort",
|
|
51
|
+
"flake8",
|
|
52
|
+
]
|
|
53
|
+
docs = [
|
|
54
|
+
"sphinx",
|
|
55
|
+
"sphinx-rtd-theme",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[project.urls]
|
|
59
|
+
Homepage = "https://github.com/jacksonhshields/geotypes"
|
|
60
|
+
Documentation = "https://jacksonhshields.github.io/geotypes/"
|
|
61
|
+
Repository = "https://github.com/jacksonhshields/geotypes.git"
|
|
62
|
+
Issues = "https://github.com/jacksonhshields/geotypes/issues"
|
|
63
|
+
|
|
64
|
+
[tool.setuptools.packages.find]
|
|
65
|
+
where = ["src"]
|
|
66
|
+
|
|
67
|
+
[tool.black]
|
|
68
|
+
line-length = 100
|
|
69
|
+
target-version = ["py38", "py39", "py310", "py311", "py312"]
|
|
70
|
+
|
|
71
|
+
[tool.isort]
|
|
72
|
+
profile = "black"
|
|
73
|
+
line_length = 100
|
geotypes-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
# Import modules
|
|
4
|
+
from . import geometry
|
|
5
|
+
|
|
6
|
+
# Export main geometry classes for convenient access
|
|
7
|
+
from .geometry import (
|
|
8
|
+
GeoType,
|
|
9
|
+
LL,
|
|
10
|
+
XY,
|
|
11
|
+
GeoPoint,
|
|
12
|
+
GeoPath,
|
|
13
|
+
GeoPoints,
|
|
14
|
+
GeoArea,
|
|
15
|
+
LocalPath,
|
|
16
|
+
LocalPoints,
|
|
17
|
+
LocalArea,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Try to import optional raster/feature modules
|
|
21
|
+
try:
|
|
22
|
+
from . import rasters
|
|
23
|
+
from . import features
|
|
24
|
+
from .rasters import Raster
|
|
25
|
+
except ImportError as e:
|
|
26
|
+
warnings.warn(f"geotypes raster capability not loaded. Error: {e}")
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"geometry",
|
|
30
|
+
"rasters",
|
|
31
|
+
"features",
|
|
32
|
+
"GeoType",
|
|
33
|
+
"LL",
|
|
34
|
+
"XY",
|
|
35
|
+
"GeoPoint",
|
|
36
|
+
"GeoPath",
|
|
37
|
+
"GeoPoints",
|
|
38
|
+
"GeoArea",
|
|
39
|
+
"LocalPath",
|
|
40
|
+
"LocalPoints",
|
|
41
|
+
"LocalArea",
|
|
42
|
+
"Raster",
|
|
43
|
+
]
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import warnings
|
|
3
|
+
from .geometry import LL, GeoPoint, GeoPoints, GeoPath, check_crs_equal
|
|
4
|
+
from .rasters import Raster, RasterRegistry
|
|
5
|
+
from typing import Union, Iterable, Optional
|
|
6
|
+
from multiprocessing import Pool, cpu_count
|
|
7
|
+
|
|
8
|
+
class FeatureExtractorBase:
|
|
9
|
+
def get_output_format(self):
|
|
10
|
+
"""
|
|
11
|
+
Returns the output format of the feature vector, where the length corresponds to the length of the feature vector.
|
|
12
|
+
i.e. if the feature vector contains [depth,slope,aspect,rugosity] this function would return ["depth","slope","aspect","rugosity"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
[str]: maps index to feature vector item.
|
|
17
|
+
"""
|
|
18
|
+
raise NotImplementedError()
|
|
19
|
+
|
|
20
|
+
def get_z(self, p: Union[LL, GeoPoint]):
|
|
21
|
+
"""
|
|
22
|
+
Gets the features at a given lat/lon
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
p: (LL,GeoPoint) Lat/lon or GeoPoint
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
np.ndarray: Features at the given location.
|
|
29
|
+
"""
|
|
30
|
+
raise NotImplementedError()
|
|
31
|
+
|
|
32
|
+
def get_zs(self, ps: GeoPoints) -> np.ndarray:
|
|
33
|
+
"""
|
|
34
|
+
Gets the features at a given set of lat/lon points
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
ps: (GeoPoints) The set of points
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
np.ndarray: The features at the given location.
|
|
41
|
+
"""
|
|
42
|
+
raise NotImplementedError()
|
|
43
|
+
|
|
44
|
+
def check_valid(self, samples: Union[GeoPoint, LL, GeoPoints, GeoPath]):
|
|
45
|
+
"""
|
|
46
|
+
Checks if a sample set is valid.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
samples: (GeoPoint, LL, GeoPoints, GeoPath) The samples to check.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
bool: True if the samples are valid, False otherwise.
|
|
53
|
+
"""
|
|
54
|
+
raise NotImplementedError()
|
|
55
|
+
|
|
56
|
+
def get_named_zs(self, p: Union[LL, GeoPoint]):
|
|
57
|
+
"""
|
|
58
|
+
Gets the features at a given lat/lon
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
p: (LL,GeoPoint) Lat/lon or GeoPoint
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
dict: Features at the given location.
|
|
65
|
+
"""
|
|
66
|
+
raise NotImplementedError()
|
|
67
|
+
|
|
68
|
+
class FeaturesFromRaster:
|
|
69
|
+
"""
|
|
70
|
+
Gathers raw features from a raster.
|
|
71
|
+
"""
|
|
72
|
+
def __init__(self, raster_registry: RasterRegistry):
|
|
73
|
+
self.raster_registry = raster_registry
|
|
74
|
+
|
|
75
|
+
def get_output_format(self):
|
|
76
|
+
"""
|
|
77
|
+
Returns the output format of the feature vector, where the length corresponds to the length of the feature vector.
|
|
78
|
+
i.e. if the feature vector contains [depth,slope,aspect,rugosity] this function would return ["depth","slope","aspect","rugosity"]
|
|
79
|
+
Returns:
|
|
80
|
+
[str]: maps index to feature vector item.
|
|
81
|
+
"""
|
|
82
|
+
return list(self.raster_registry.keys())
|
|
83
|
+
|
|
84
|
+
def get_z(self, p: Union[LL, GeoPoint]):
|
|
85
|
+
"""
|
|
86
|
+
Gets the features at a given lat/lon
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
p: (LL,GeoPoint) Lat/lon or GeoPoint
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
np.ndarray: Features at the given location.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
# Checks for either LL or GeoPoint
|
|
96
|
+
if isinstance(p, LL):
|
|
97
|
+
use_ll = True
|
|
98
|
+
elif isinstance(p, GeoPoint):
|
|
99
|
+
use_ll = False
|
|
100
|
+
else:
|
|
101
|
+
raise ValueError("p must be LL or GeoPoint")
|
|
102
|
+
|
|
103
|
+
feats = []
|
|
104
|
+
data_ok = True
|
|
105
|
+
# iterate over the rasters and get the values
|
|
106
|
+
for rname,ritem in self.raster_registry.items():
|
|
107
|
+
if use_ll:
|
|
108
|
+
v = ritem.dataset.get_value_at_ll(p, band=ritem.band) # check if the value is valid
|
|
109
|
+
else:
|
|
110
|
+
v = ritem.dataset.get_value_at_geo(p, band=ritem.band)
|
|
111
|
+
# v = ritem.dataset.get_value(p)
|
|
112
|
+
|
|
113
|
+
if v is None: # if not, then we can't use this point
|
|
114
|
+
data_ok = False
|
|
115
|
+
break
|
|
116
|
+
feats.append(v)
|
|
117
|
+
if not data_ok: # if we couldn't get the data, then return None
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
feat_arr = np.array(feats)
|
|
121
|
+
# feat_arr = np.concatenate(feats, axis=-1) # concatenate the features
|
|
122
|
+
feat_arr = feat_arr.squeeze() # squeeze the features
|
|
123
|
+
return feat_arr # return the features
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def get_zs(self, ps: GeoPoints) -> np.ndarray:
|
|
127
|
+
"""
|
|
128
|
+
Gets the features at a given set of lat/lon points
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
ps: (GeoPoints) The set of points
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
np.ndarray: The features at the given location.
|
|
135
|
+
"""
|
|
136
|
+
features = []
|
|
137
|
+
for rname, ritem in self.raster_registry.items():
|
|
138
|
+
if check_crs_equal(ritem.dataset.dataset.crs, ps.crs):
|
|
139
|
+
ps_crs = ps
|
|
140
|
+
else:
|
|
141
|
+
ps_crs = ps.to_crs(ritem.dataset.dataset.crs)
|
|
142
|
+
|
|
143
|
+
features.append(np.array(ritem.dataset.get_value(ps_crs)).squeeze())
|
|
144
|
+
|
|
145
|
+
if len(features) == 0:
|
|
146
|
+
return None
|
|
147
|
+
return np.asarray(features).T
|
|
148
|
+
|
|
149
|
+
def check_valid(self, samples: Union[GeoPoint, LL, GeoPoints, GeoPath]):
|
|
150
|
+
"""
|
|
151
|
+
Checks if a sample set is valid.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
samples: (GeoPoint, LL, GeoPoints, GeoPath) The samples to check.
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
bool: True if the samples are valid, False otherwise.
|
|
158
|
+
"""
|
|
159
|
+
if isinstance(samples, (GeoPoint, LL)):
|
|
160
|
+
samples = [samples]
|
|
161
|
+
for p in samples:
|
|
162
|
+
if self.get_z(p) is None:
|
|
163
|
+
return False
|
|
164
|
+
return True
|