geojson
既存のライブラリが重厚長大なものが多いのだけど、 OpenGL や SVG のような二次元のベクター描画で簡単に済ませたい。 その方向で調査。
format
unknown: blockquote => {"type":"blockquote","children":[{"type":"paragraph","children":[{"type":"text","value":"GeoJSON is a geospatial data","position":{"start":{"line":14,"column":3,"offset":231},"end":{"line":14,"column":31,"offset":259}}}],"position":{"start":{"line":14,"column":3,"offset":231},"end":{"line":14,"column":31,"offset":259}}}],"position":{"start":{"line":14,"column":1,"offset":229},"end":{"line":14,"column":31,"offset":259}}}
以下のような様式。 Feature の中に Geometry が入っている。
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {}
}
]
}
unknown: table => {"type":"table","align":[null],"children":[{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"Geometry Object","position":{"start":{"line":32,"column":3,"offset":489},"end":{"line":32,"column":18,"offset":504}}}],"position":{"start":{"line":32,"column":1,"offset":487},"end":{"line":32,"column":23,"offset":509}}}],"position":{"start":{"line":32,"column":1,"offset":487},"end":{"line":32,"column":23,"offset":509}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"Point","position":{"start":{"line":34,"column":3,"offset":535},"end":{"line":34,"column":8,"offset":540}}}],"position":{"start":{"line":34,"column":1,"offset":533},"end":{"line":34,"column":23,"offset":555}}}],"position":{"start":{"line":34,"column":1,"offset":533},"end":{"line":34,"column":23,"offset":555}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"MultiPoint","position":{"start":{"line":35,"column":3,"offset":558},"end":{"line":35,"column":13,"offset":568}}}],"position":{"start":{"line":35,"column":1,"offset":556},"end":{"line":35,"column":23,"offset":578}}}],"position":{"start":{"line":35,"column":1,"offset":556},"end":{"line":35,"column":23,"offset":578}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"LineString","position":{"start":{"line":36,"column":3,"offset":581},"end":{"line":36,"column":13,"offset":591}}}],"position":{"start":{"line":36,"column":1,"offset":579},"end":{"line":36,"column":23,"offset":601}}}],"position":{"start":{"line":36,"column":1,"offset":579},"end":{"line":36,"column":23,"offset":601}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"MultiLineString","position":{"start":{"line":37,"column":3,"offset":604},"end":{"line":37,"column":18,"offset":619}}}],"position":{"start":{"line":37,"column":1,"offset":602},"end":{"line":37,"column":23,"offset":624}}}],"position":{"start":{"line":37,"column":1,"offset":602},"end":{"line":37,"column":23,"offset":624}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"Polygon","position":{"start":{"line":38,"column":3,"offset":627},"end":{"line":38,"column":10,"offset":634}}}],"position":{"start":{"line":38,"column":1,"offset":625},"end":{"line":38,"column":23,"offset":647}}}],"position":{"start":{"line":38,"column":1,"offset":625},"end":{"line":38,"column":23,"offset":647}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"MultiPolygon","position":{"start":{"line":39,"column":3,"offset":650},"end":{"line":39,"column":15,"offset":662}}}],"position":{"start":{"line":39,"column":1,"offset":648},"end":{"line":39,"column":23,"offset":670}}}],"position":{"start":{"line":39,"column":1,"offset":648},"end":{"line":39,"column":23,"offset":670}}},{"type":"tableRow","children":[{"type":"tableCell","children":[{"type":"text","value":"GeometryCollection","position":{"start":{"line":40,"column":3,"offset":673},"end":{"line":40,"column":21,"offset":691}}}],"position":{"start":{"line":40,"column":1,"offset":671},"end":{"line":40,"column":23,"offset":693}}}],"position":{"start":{"line":40,"column":1,"offset":671},"end":{"line":40,"column":23,"offset":693}}}],"position":{"start":{"line":32,"column":1,"offset":487},"end":{"line":40,"column":23,"offset":693}}}
Point
{
"type": "Point",
"coordinates": [100.0, 0.0]
}
Polygon
穴が空いている場合は、複数の頂点リストを保持する。
// No holes:
{
"type": "Polygon",
"coordinates": [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
]
]
}
// with holes:
{
"type": "Polygon",
"coordinates": [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0]
],
[
[100.8, 0.8],
[100.8, 0.2],
[100.2, 0.2],
[100.2, 0.8],
[100.8, 0.8]
]
]
}
read
python で素直に読んでみた。
import pathlib
import json
def process_geometry(geometry: dict):
match geometry:
case {"type": "MultiPolygon", "coordinates": coordinates}:
print(f'{len(coordinates)} polygon')
for coord in coordinates:
print(f' {len(coord)} rings')
for x in coord:
print(f' {len(x)} points')
def process_feature(feature: dict):
match feature:
case {"type": "Feature", "properties": props, "geometry": geometry}:
print(props)
process_geometry(geometry)
case _:
raise NotImplementedError()
def main(path: pathlib.Path):
data = json.loads(path.read_bytes())
match data:
case {"type": "FeatureCollection", "features": features}:
for feature in features:
process_feature(feature)
if __name__ == '__main__':
main(pathlib.Path('japan.geo.json'))
jpan.geo.json は、 https://github.com/dataofjapan/land です。 実行結果。
{'nam': 'Kyoto Fu', 'nam_ja': '京都府', 'id': 26}
4 polygon
1 rings
1235 points
1 rings
6 points
1 rings
8 points
1 rings
6 points
なるほど。
GL_LINE_LOOP
単純に GL_LINE_LOOP で描画できそうとわかった。
def process_geometry(geometry: dict) -> Polygon:
match geometry:
case {"type": "Polygon", "coordinates": polygon}:
assert len(polygon) == 1
array = (float2 * len(polygon[0]))()
for i, (x, y) in enumerate(polygon[0]):
array[i] = float2(x, y)
return Polygon(array, [SubMesh(0, len(array))])
case {"type": "MultiPolygon", "coordinates": polygons}:
array = (float2 * sum(len(polygon[0]) for polygon in polygons))()
i = 0
submeshes = []
for polygon in polygons:
assert len(polygon) == 1
submeshes.append(SubMesh(i, len(polygon[0])))
for (x, y) in polygon[0]:
array[i] = float2(x, y)
i += 1
return Polygon(array, submeshes)
case _:
raise NotImplementedError()
orthogonal の方で適当にビューポートを (140, 35) というような適当な経度緯度に調整してやればよさそう。