Skip to content

Commit 9205a44

Browse files
cropping geojson (features) (#23)
* update manylinux * update * update headers * init cropping * not ready * bbox filtering * crop geojson * nice * update headers * add crop * not ready * export bbox * update version --------- Co-authored-by: TANG ZHIXIONG <zhixiong.tang@momenta.ai>
1 parent ed0fd3e commit 9205a44

File tree

9 files changed

+466
-12
lines changed

9 files changed

+466
-12
lines changed

Makefile

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ docs_serve:
8585
mkdocs serve -a 0.0.0.0:8088
8686

8787
DOCKER_TAG_WINDOWS ?= ghcr.io/cubao/build-env-windows-x64:v0.0.1
88-
DOCKER_TAG_LINUX ?= ghcr.io/cubao/build-env-manylinux2014-x64:v0.0.1
88+
DOCKER_TAG_LINUX ?= ghcr.io/cubao/build-env-manylinux2014-x64:v0.0.4
8989
DOCKER_TAG_MACOS ?= ghcr.io/cubao/build-env-macos-arm64:v0.0.1
9090

9191
test_in_win:
@@ -95,6 +95,15 @@ test_in_mac:
9595
test_in_linux:
9696
docker run --rm -w `pwd` -v `pwd`:`pwd` -v `pwd`/build/linux:`pwd`/build -it $(DOCKER_TAG_LINUX) bash
9797

98+
DEV_CONTAINER_NAME ?= $(USER)_$(subst /,_,$(PROJECT_NAME)____$(PROJECT_SOURCE_DIR))
99+
DEV_CONTAINER_IMAG ?= $(DOCKER_TAG_LINUX)
100+
test_in_dev_container:
101+
docker ps | grep $(DEV_CONTAINER_NAME) \
102+
&& docker exec -it $(DEV_CONTAINER_NAME) bash \
103+
|| docker run --rm --name $(DEV_CONTAINER_NAME) \
104+
--network host --security-opt seccomp=unconfined \
105+
-v `pwd`:`pwd` -w `pwd` -it $(DEV_CONTAINER_IMAG) bash
106+
98107
PYTHON ?= python3
99108
python_install:
100109
$(PYTHON) setup.py install --force
@@ -155,7 +164,7 @@ python_build_py310:
155164
PYTHON=python conda run --no-capture-output -n py310 make python_build
156165
python_build_all: python_build_py36 python_build_py37 python_build_py38 python_build_py39 python_build_py310
157166
python_build_all_in_linux:
158-
docker run --rm -w `pwd` -v `pwd`:`pwd` -v `pwd`/build/win:`pwd`/build -it $(DOCKER_TAG_LINUX) make python_build_all
167+
docker run --rm -w `pwd` -v `pwd`:`pwd` -v `pwd`/build/linux:`pwd`/build -it $(DOCKER_TAG_LINUX) make python_build_all
159168
make repair_wheels && rm -rf dist/*.whl && mv wheelhouse/*.whl dist && rm -rf wheelhouse
160169
python_build_all_in_macos: python_build_py38 python_build_py39 python_build_py310
161170
python_build_all_in_windows: python_build_all

docs/about/release-notes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ To upgrade `pybind11-geobuf` to the latest version, use pip:
1010
pip install -U pybind11-geobuf
1111
```
1212

13+
## Version 0.1.6 (2023-07-02)
14+
15+
* Crop geojson features by polygon (alpha release)
16+
1317
## Version 0.1.5 (2023-06-02)
1418

1519
* Add `round_non_geojson` to `normalize_json`

headers

Submodule headers updated 75 files

mkdocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ site_description: A compact binary encoding for geographic data, with python bin
44
site_author: district10
55

66
repo_url: https://github.com/cubao/pybind11-geobuf
7-
edit_uri: blob/master/docs/
7+
edit_uri: blob/dev/docs/
88

99
theme: readthedocs
1010
nav:

pybind11_geobuf/crop.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import os
2+
from typing import List, Optional, Tuple, Union # noqa
3+
4+
import numpy as np
5+
from _pybind11_geobuf import geojson, tf
6+
from loguru import logger
7+
8+
9+
def bbox2polygon(bbox: np.ndarray):
10+
lon0, lat0, lon1, lat1 = bbox
11+
return np.array(
12+
[
13+
[lon0, lat0, 0.0],
14+
[lon1, lat0, 0.0],
15+
[lon1, lat1, 0.0],
16+
[lon0, lat1, 0.0],
17+
[lon0, lat0, 0.0],
18+
]
19+
)
20+
21+
22+
def crop_by_feature_id(
23+
input_path: str,
24+
output_path: str,
25+
*,
26+
feature_id: str,
27+
buffer: Union[float, Tuple[float, float]] = 100.0,
28+
clipping_mode: str = "longest",
29+
max_z_offset: float = None,
30+
) -> bool:
31+
if not feature_id:
32+
logger.info(
33+
f"invalid feature id: {feature_id} (type: {type(feature_id)})"
34+
) # noqa
35+
return False
36+
g = geojson.GeoJSON().load(input_path)
37+
if not g.is_feature_collection():
38+
logger.warning(f"{input_path} is not valid GeoJSON FeatureCollection")
39+
return False
40+
41+
fc = g.as_feature_collection()
42+
bbox = None
43+
height = None
44+
for f in fc:
45+
props = f.properties()
46+
if "id" not in props:
47+
continue
48+
fid = props["id"]()
49+
if fid == feature_id:
50+
bbox = f.bbox()
51+
if max_z_offset is not None:
52+
height = f.bbox(with_z=True)[2::3].mean()
53+
if bbox is None:
54+
logger.error(f"not any feature matched by id: {feature_id}")
55+
return False
56+
57+
dlon, dlat = 1.0 / tf.cheap_ruler_k(bbox[1::2].mean())[:2]
58+
if isinstance(buffer, (int, float, np.generic)):
59+
dlon *= buffer
60+
dlat *= buffer
61+
else:
62+
dlon *= buffer[0]
63+
dlat *= buffer[1]
64+
bbox += [-dlon, -dlat, dlon, dlat]
65+
logger.info(f"bbox: {bbox}")
66+
67+
polygon = bbox2polygon(bbox)
68+
if height is not None:
69+
polygon[:, 2] = height
70+
logger.info(f"polygon:\n{polygon}")
71+
72+
logger.info(f"writing to {output_path} ...")
73+
os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
74+
cropped = g.crop(
75+
polygon,
76+
clipping_mode=clipping_mode,
77+
max_z_offset=max_z_offset,
78+
)
79+
return cropped.to_rapidjson().sort_keys().dump(output_path, indent=True)
80+
81+
82+
def crop_by_grid(
83+
input_path: str,
84+
output_dir: str,
85+
*,
86+
anchor_lla: Union[str, List[float]] = None,
87+
grid_size: Union[float, Tuple[float, float]] = 1000.0,
88+
):
89+
os.makedirs(os.path.abspath(output_dir), exist_ok=True)
90+
91+
92+
def crop_by_center(
93+
input_path: str,
94+
output_dir: str,
95+
*,
96+
anchor_lla: Union[str, List[float]] = None,
97+
size: Union[float, Tuple[float, float]] = 1000.0,
98+
):
99+
os.makedirs(os.path.abspath(output_dir), exist_ok=True)
100+
101+
102+
def crop_by_bbox(
103+
input_path: str,
104+
output_path: str,
105+
*,
106+
bbox: Union[str, List[float]],
107+
z_center: float = None,
108+
z_max_offset: float = None,
109+
):
110+
logger.info(f"wrote to {output_path}")
111+
112+
113+
def crop_by_polygon(
114+
input_path: str,
115+
output_path: str,
116+
*,
117+
polygon: Union[str, np.ndarray],
118+
z_max_offset: float = None,
119+
):
120+
pass
121+
122+
123+
if __name__ == "__main__":
124+
import fire
125+
126+
fire.core.Display = lambda lines, out: print(*lines, file=out)
127+
fire.Fire(
128+
{
129+
"by_feature_id": crop_by_feature_id,
130+
"by_grid": crop_by_grid,
131+
"by_center": crop_by_center,
132+
"by_bbox": crop_by_bbox,
133+
"by_polygon": crop_by_polygon,
134+
}
135+
)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def build_extension(self, ext):
127127
# logic and declaration, and simpler if you include description/version in a file.
128128
setup(
129129
name="pybind11_geobuf",
130-
version="0.1.5",
130+
version="0.1.6",
131131
author="tzx",
132132
author_email="dvorak4tzx@gmail.com",
133133
url="https://geobuf-cpp.readthedocs.io",

0 commit comments

Comments
 (0)