forked from OSGeo/gdal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathogr_CityJSON.py
179 lines (161 loc) · 6.65 KB
/
ogr_CityJSON.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#!/usr/bin/env python
###############################################################################
#
# Purpose: CityJSON OGR driver
# Author: Even Rouault <even dot rouault at spatialys.com>
#
###############################################################################
# Copyright (c) 2019, Even Rouault <even dot rouault at spatialys.com>
# SPDX-License-Identifier: MIT
###############################################################################
# Metadata parsed by GDAL C++ code at driver pre-loading, starting with '# gdal: '
# gdal: DRIVER_NAME = "CityJSON"
# gdal: DRIVER_SUPPORTED_API_VERSION = [1]
# gdal: DRIVER_DCAP_VECTOR = "YES"
# gdal: DRIVER_DCAP_VIRTUALIO = "YES"
# gdal: DRIVER_DMD_LONGNAME = "CityJSON"
# gdal: DRIVER_DMD_EXTENSIONS = "json"
import json
import os
from gdal_python_driver import BaseDataset, BaseDriver, BaseLayer
class Layer(BaseLayer):
def __init__(self, filename, content):
self.content = content
self.name = os.path.splitext(os.path.basename(filename))[0]
self.fields = [
{"name": "id"},
{"name": "type"},
]
self.count = 0
set_attrs = set()
for key_value in content["CityObjects"].items():
value = key_value[1]
if "attributes" in value:
for kv in value["attributes"].items():
if not kv[0] in set_attrs:
set_attrs.add(kv[0])
v = kv[1]
t = "String"
if isinstance(v, int):
t = "Integer"
elif isinstance(v, float):
t = "Real"
elif "Date" in kv[0]:
t = "Date"
self.fields.append({"name": kv[0], "type": t})
for geom in value["geometry"]:
if geom["type"] in ("Solid", "MultiSurface"):
self.count += 1
md = content["metadata"]
srs = md["referenceSystem"] if "referenceSystem" in md else None
if not srs and "crs" in md:
if isinstance(md["crs"], dict) and "epsg" in md["crs"]:
srs = "EPSG:" + str(md["crs"]["epsg"])
self.geometry_fields = [{"type": "MultiPolygonZ", "srs": srs}]
if "transform" in content:
self.translate = content["transform"]["translate"]
self.scale = content["transform"]["scale"]
else:
self.translate = [0, 0, 0]
self.scale = [1, 1, 1]
self.vertices = content["vertices"]
def extent(self, force):
md = self.content["metadata"]
if "geographicalExtent" in md:
bbox = md["geographicalExtent"]
else:
bbox = md["bbox"]
return [bbox[0], bbox[1], bbox[3], bbox[4]]
def feature_count(self, force):
return self.count
def __iter__(self):
fid = 1
vertices = self.vertices
scale = self.scale
translate = self.translate
for key_value in self.content["CityObjects"].items():
value = key_value[1]
for geom in value["geometry"]:
geom_in = None
boundaries = geom["boundaries"]
if geom["type"] == "Solid":
assert len(boundaries) == 1, (len(boundaries), key_value)
geom_in = boundaries[0]
elif geom["type"] == "MultiSurface":
geom_in = boundaries
if geom_in:
out_mpoly = []
for poly in geom_in:
out_poly = []
for ring in poly:
out_ring = []
for vertex_idx in ring:
v = vertices[vertex_idx]
c = [v * s + t for v, s, t in zip(v, scale, translate)]
out_ring.append(c)
out_ring.append(out_ring[0])
out_poly.append(out_ring)
out_mpoly.append(out_poly)
properties = {"id": key_value[0], "type": value["type"]}
if "attributes" in value:
properties.update(value["attributes"])
wkt = "MULTIPOLYGON Z("
first_poly = True
for poly in out_mpoly:
if not first_poly:
wkt += ","
else:
first_poly = False
wkt += "("
first_ring = True
for ring in poly:
if not first_ring:
wkt += ","
else:
first_ring = False
wkt += "("
first_vertex = True
for v in ring:
if not first_vertex:
wkt += ","
else:
first_vertex = False
wkt += "%.18g %.18g %.18g" % (v[0], v[1], v[2])
wkt += ")"
wkt += ")"
wkt += ")"
f = {
"type": "OGRFeature",
"id": fid,
"fields": properties,
"geometry_fields": {"": wkt},
}
yield f
fid += 1
class Dataset(BaseDataset):
def __init__(self, filename, content):
self.layers = [Layer(filename, content)]
class Driver(BaseDriver):
def identify(self, filename, first_bytes, open_flags, open_options={}):
return (
b'"type":"CityJSON"' in first_bytes or b'"type": "CityJSON"' in first_bytes
)
def open(self, filename, first_bytes, open_flags, open_options={}):
if not self.identify(filename, first_bytes, open_flags):
return None
if filename.startswith("/vsi"):
from osgeo import gdal
f = gdal.VSIFOpenL(filename, "rb")
if not f:
return None
gdal.VSIFSeekL(f, 0, 2)
length = gdal.VSIFTellL(f)
gdal.VSIFSeekL(f, 0, 0)
try:
content = json.loads(gdal.VSIFReadL(1, length, f))
finally:
gdal.VSIFCloseL(f)
else:
with open(filename, "rt") as f:
content = json.loads(f.read())
return Dataset(filename, content)