#!/usr/bin/env python3 """Turn DataV administrative-boundary GeoJSON into the compact layers the map catalogue loads. Usage: python3 -I Tools/prepare-region-maps.py Content python3 -I Tools/prepare-region-maps.py --taiwan-counties Content Only outer rings are kept (the runtime draws filled outlines), coordinates are rounded to 4 decimals (about 10 m) and exact repeats removed. Nothing is invented, moved or smoothed. The national file's unnamed feature is the South China Sea dashed line and is kept as its own kind so it is always drawn. """ import hashlib, json, os, sys SOURCES = { "100000_full.json": ("map.china-provinces.json", None), "440000_full.json": ("map.guangdong-cities.json", None), "130000_full.json": ("map.hebei-cities.json", None), "510000_full.json": ("map.sichuan-cities.json", None), "320000_full.json": ("map.jiangsu-cities.json", None), "330000_full.json": ("map.zhejiang-cities.json", None), } def rings(geometry): polygons = [geometry["coordinates"]] if geometry["type"] == "Polygon" else geometry["coordinates"] out = [] for polygon in polygons: ring, flat, last = polygon[0], [], None for lon, lat in ring: point = (round(lon, 4), round(lat, 4)) if point != last: flat.extend(point); last = point if len(flat) >= 6: out.append(flat) return out # Taiwan's cities and counties come from geoBoundaries (gbOpen TWN ADM1, built from OpenStreetMap), which is # licensed under the Open Database License 1.0: the output keeps that licence and its attribution. Names are # given in Chinese by ISO 3166-2 code. Following the administrative convention used by the rest of this # edition's maps, Kinmen and the Matsu Islands are not listed under Taiwan, and the Pratas (Dongsha) polygon # attached to Kaohsiung in the source is left out; nothing else is removed, moved or smoothed. TAIWAN_SOURCE = "https://github.com/wmgeolab/geoBoundaries/raw/9469f09/releaseData/gbOpen/TWN/ADM1/geoBoundaries-TWN-ADM1_simplified.geojson" TAIWAN_NAMES = { "TW-TPE": "台北市", "TW-NWT": "新北市", "TW-KEE": "基隆市", "TW-TAO": "桃园市", "TW-HSZ": "新竹市", "TW-HSQ": "新竹县", "TW-MIA": "苗栗县", "TW-TXG": "台中市", "TW-CHA": "彰化县", "TW-NAN": "南投县", "TW-YUN": "云林县", "TW-CYI": "嘉义市", "TW-CYQ": "嘉义县", "TW-TNN": "台南市", "TW-KHH": "高雄市", "TW-PIF": "屏东县", "TW-ILA": "宜兰县", "TW-HUA": "花莲县", "TW-TTT": "台东县", "TW-PEN": "澎湖县", } TAIWAN_NOT_LISTED = {"TW-KIN", "TW-LIE"} def taiwan_counties(path, target): raw = open(path, "rb").read() features, seen = [], set() for feature in json.loads(raw)["features"]: code = feature["properties"]["shapeISO"] if code in TAIWAN_NOT_LISTED: continue seen.add(code) kept = [ring for ring in rings(feature["geometry"]) if min(ring[0::2]) >= 119.0] features.append({"name": TAIWAN_NAMES[code], "adcode": code, "kind": "area", "rings": kept}) if seen != set(TAIWAN_NAMES): raise SystemExit("unexpected units in source: %s" % sorted(seen ^ set(TAIWAN_NAMES))) features.sort(key=lambda f: list(TAIWAN_NAMES).index(f["adcode"])) text = json.dumps({"source": TAIWAN_SOURCE, "license": "Open Database License (ODbL) 1.0", "attribution": "© OpenStreetMap contributors, via geoBoundaries", "features": features}, ensure_ascii=False, separators=(",", ":")) with open(os.path.join(target, "map.taiwan-counties.json"), "w", encoding="utf-8") as handle: handle.write(text) points = sum(len(r) // 2 for f in features for r in f["rings"]) print("%s sha256=%s -> map.taiwan-counties.json: %d features, %d points, %d bytes" % (os.path.basename(path), hashlib.sha256(raw).hexdigest(), len(features), points, len(text.encode("utf-8")))) def main(source, target): report = [] for name, (output, _) in SOURCES.items(): path = os.path.join(source, name) if not os.path.exists(path): continue # convert whichever source files this folder holds raw = open(path, "rb").read() features = [] for feature in json.loads(raw)["features"]: properties = feature["properties"] if not feature.get("geometry"): continue label = properties.get("name") or "" features.append({"name": label, "adcode": str(properties.get("adcode", "")), "kind": "area" if label else "sea-line", "rings": rings(feature["geometry"])}) text = json.dumps({"source": "https://geo.datav.aliyun.com/areas_v3/bound/" + name, "features": features}, ensure_ascii=False, separators=(",", ":")) with open(os.path.join(target, output), "w", encoding="utf-8") as handle: handle.write(text) points = sum(len(r) // 2 for f in features for r in f["rings"]) report.append((name, hashlib.sha256(raw).hexdigest(), output, len(features), points, len(text.encode("utf-8")))) for row in report: print("%s sha256=%s -> %s: %d features, %d points, %d bytes" % row) if __name__ == "__main__": if sys.argv[1] == "--taiwan-counties": taiwan_counties(sys.argv[2], sys.argv[3]) else: main(sys.argv[1], sys.argv[2])