dc-l1-tokyo-ring-orientation
Map (reference ← swipe → agent)
0:00
tokyo_buildings_legacy.geojson is a footprint export from an old in-house tool. When we load it into web-based GeoJSON viewers, building interiors are shaded incorrectly: holes appear filled and exteriors render as empty space. Our tile-server import script also rejects every feature with geometry warnings. Fix the geometries so that standard GeoJSON web viewers render them correctly. Attributes must be untouched. Output to tokyo_buildings_fixed.geojson.
Every feature must remain a Polygon with interior rings and holes preserved exactly as they are; do not flatten them. feature_id is the join key.
0:00
System info
{
"adapter": "openrouter",
"model": "google/gemma-4-26b-a4b-it",
"base_url": "https://openrouter.ai/api/v1"
}
0:01
Bash
ls -lh tokyo_buildings_legacy.geojson
-rw-r--r-- 1 agent agent 89K Jun 16 12:11 tokyo_buildings_legacy.geojson
0:04
Bash
head -n 20 tokyo_buildings_legacy.geojson
{
"type": "FeatureCollection",
"name": "tokyo_buildings_legacy",
"crs": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
139.7029081,
35.6592372
],
0:10
Write
solve.py
content · 52 lines · py
import geopandas as gpd
# Load the data
gdf = gpd.read_file('tokyo_buildings_legacy.geojson')
# Check the first few features' geometries to see if they have holes or inverted rings
print("First 5 geometries:")
for i, geom in enumerate(gdf.geometry.head()):
print(f"{i}: {geom}")
# In GeoJSON, the exterior ring should be counter-clockwise (for some standards)
# or follow the Right Hand Rule.
# However, the most common issue with "inverted" shading in web viewers
# is the orientation of the rings.
# Standard GeoJSON (RFC 7946) specifies that exterior rings should be counter-clockwise
# and interior rings clockwise.
# Shapely/GeoPandas usually follows the Right Hand Rule (exterior is CCW).
# Let's try to fix the orientation of the rings.
def fix_orientation(geom):
if geom.is_empty:
return geom
if geom.geom_type == 'Polygon':
# orient() ensures exterior is CCW and interior rings are CW
from shapely.geometry import Polygon
return geom.buffer(0).simplify(0) # Sometimes a buffer(0) fixes self-intersections
return geom
# Actually, the description says "holes appear filled and exteriors render as empty space".
# This is a classic symptom of inverted ring winding.
# In Shapely, the first ring in coordinates is the exterior.
def fix_ring_winding(polygon):
if not polygon.interiors:
# It's a simple polygon. Ensure it's valid.
return polygon.simplify(0)
# A more robust way to fix winding in Shapely is to use the fact that
# orient(polygon, sign=1.0) makes exterior CCW and interior CW.
from shapely.ops import orient
return orient(polygon, sign=1.0)
# Let's apply orient to each geometry
gdf['geometry'] = gdf['geometry'].apply(lambda x: fix_ring_winding(x) if x.geom_type == 'Polygon' else x)
# Check if it's still valid
print(f"Invalid geometries after fix: {gdf.geometry.is_valid.value_counts().get(False, 0)}")
# Save the result
gdf.to_file('tokyo_buildings_fixed.geojson', driver='GeoJSON')
print("Saved to tokyo_buildings_fixed.geojson")
Wrote solve.py (2056 chars).
0:12
Bash
python solve.py
First 5 geometries: 0: POLYGON ((139.7029081 35.6592372, 139.7027971 35.659202, 139.7027755 35.6592471, 139.7028864 35.6592823, 139.7029081 35.6592372)) 1: POLYGON ((139.7010896 35.6601981, 139.7010851 35.660084, 139.7010559 35.660088, 139.7010511 35.6600707, 139.7009769 35.6600834, 139.7009923 35.6601243, 139.7009007 35.6601417, 139.7009195 35.6602237, 139.7010896 35.6601981)) 2: POLYGON ((139.7011168 35.6616718, 139.7009949 35.6616685, 139.7009926 35.6617229, 139.7011145 35.6617263, 139.7011168 35.6616718)) 3: POLYGON ((139.7032678 35.6601827, 139.7032982 35.660114, 139.7032205 35.6600913, 139.7031901 35.66016, 139.7032678 35.6601827)) 4: POLYGON ((139.7015768 35.6592033, 139.701614 35.6592118, 139.7016492 35.6591105, 139.7015748 35.6590934, 139.7015395 35.6591947, 139.7015768 35.6592033)) Invalid geometries after fix: 0 Saved to tokyo_buildings_fixed.geojson
0:17
Write
solve.py
content · 44 lines · py
import geopandas as gpd
from shapely.ops import orient
# Load the data
gdf = gpd.read_file('tokyo_buildings_legacy.geojson')
print("Initial validity check:")
print(gdf.geometry.is_valid.value_counts())
def fix_geometry(geom):
if geom.is_empty:
return geom
# First, ensure it's valid. buffer(0) is a common trick for fixing self-intersections
# which might be causing the "geometry warnings" in the tile server.
if not geom.is_valid:
geom = geom.buffer(0)
if geom.geom_type == 'Polygon':
# The prompt says "holes appear filled and exteriors render as empty space".
# This is exactly what happens when the exterior ring is CW and interior is CCW
# (or vice versa depending on the renderer's winding rule).
# RFC 7946 says exterior should be CCW.
# orient(geom, sign=1.0) makes exterior CCW and interior CW.
return orient(geom, sign=1.0)
elif geom.geom_type == 'MultiPolygon':
# For MultiPolygon, we orient each part
from shapely.geometry import MultiPolygon
new_parts = [orient(p, sign=1.0) for p in geom.geoms]
return MultiPolygon(new_parts)
return geom
# Apply fix
gdf['geometry'] = gdf['geometry'].apply(fix_geometry)
print("\nFinal validity check:")
print(gdf.geometry.is_valid.value_counts())
# Ensure we keep all original attributes and save
gdf.to_file('tokyo_buildings_fixed.geojson', driver='GeoJSON')
print("\nSaved to tokyo_buildings_fixed.geojson")
Wrote solve.py (1509 chars).
0:19
Bash
python solve.py
Initial validity check: True 100 Name: count, dtype: int64 Final validity check: True 100 Name: count, dtype: int64 Saved to tokyo_buildings_fixed.geojson
0:33
Bash
# Let's check if there were any invalid geometries in the ORIGINAL file.
# Let's check if there were any invalid geometries in the ORIGINAL file. # My previous script printed "Initial validity check" but it seems it was already valid? # Wait, if it's already valid, why the shading issue? # Ah, "valid" in Shapely means it follows the rules of a valid polygon (no self-intersections), # but "valid" for GeoJSON/Web-viewers also implies correct winding (Right Hand Rule / RFC 7946). # If the input was "valid" but the rings were inverted, it would still be "valid" in Shapely terms # but render incorrectly. # Let's verify the content of the output file to ensure it's a valid GeoJSON. head -n 30 tokyo_buildings_fixed.geojson
{
"type": "FeatureCollection",
"name": "tokyo_buildings_fixed",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "feature_id": 1, "overture_id": "01302ced-3141-4b64-b630-98019352a9fd", "name_primary": "渋谷協和ビル", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7029081, 35.6592372 ], [ 139.702886400000011, 35.6592823 ], [ 139.7027755, 35.6592471 ], [ 139.7027971, 35.659202 ], [ 139.7029081, 35.6592372 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 2, "overture_id": "046ab130-a9a2-4a0e-9142-b7ea36374fdb", "name_primary": "丸大ビル", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.701089599999989, 35.6601981 ], [ 139.7009195, 35.6602237 ], [ 139.7009007, 35.6601417 ], [ 139.7009923, 35.6601243 ], [ 139.7009769, 35.6600834 ], [ 139.7010511, 35.6600707 ], [ 139.7010559, 35.660088 ], [ 139.7010851, 35.660084 ], [ 139.701089599999989, 35.6601981 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 3, "overture_id": "063348bb-5559-4839-bf7d-8c6fca154867", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7011168, 35.6616718 ], [ 139.701114499999989, 35.6617263 ], [ 139.7009926, 35.6617229 ], [ 139.700994900000012, 35.6616685 ], [ 139.7011168, 35.6616718 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 4, "overture_id": "0a1085e0-b946-40d4-8b22-17f5110beee6", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7032678, 35.6601827 ], [ 139.7031901, 35.66016 ], [ 139.703220499999986, 35.6600913 ], [ 139.7032982, 35.660114 ], [ 139.7032678, 35.6601827 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 5, "overture_id": "0c2ac2ae-6836-429c-888f-b33dba5cd074", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7015768, 35.6592033 ], [ 139.7015395, 35.6591947 ], [ 139.7015748, 35.6590934 ], [ 139.7016492, 35.6591105 ], [ 139.701614, 35.6592118 ], [ 139.7015768, 35.6592033 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 6, "overture_id": "0ff95784-5c37-4c0e-8ee3-674f96075b8c", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7025726, 35.661211 ], [ 139.7024485, 35.6612117 ], [ 139.7024481, 35.6611726 ], [ 139.7025916, 35.6611717 ], [ 139.7025917, 35.6611836 ], [ 139.7027837, 35.6611824 ], [ 139.702784400000013, 35.6612685 ], [ 139.702573199999989, 35.6612697 ], [ 139.7025726, 35.661211 ] ], [ [ 139.70261625, 35.6612158 ], [ 139.70261625, 35.6612207 ], [ 139.702633065000015, 35.6612207 ], [ 139.702633065000015, 35.6612158 ], [ 139.70261625, 35.6612158 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 7, "overture_id": "11b3ca95-3d26-4876-b2a0-c82b792992ec", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7015979, 35.6600884 ], [ 139.7016004, 35.6601443 ], [ 139.7015731, 35.6601452 ], [ 139.7015706, 35.6600892 ], [ 139.7015979, 35.6600884 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 8, "overture_id": "12f66548-a7b0-491d-b900-32b6cbe7fe79", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.702111800000011, 35.6613063 ], [ 139.702051399999988, 35.6613056 ], [ 139.702046599999989, 35.6612494 ], [ 139.7021249, 35.6612491 ], [ 139.702111800000011, 35.6613063 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 9, "overture_id": "1b3f9098-7a2a-4dfb-b30c-baf5c9fd1822", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.701766, 35.6593673 ], [ 139.701749799999988, 35.6593598 ], [ 139.701775199999986, 35.6593235 ], [ 139.701807699999989, 35.6593385 ], [ 139.7017823, 35.6593748 ], [ 139.701766, 35.6593673 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 10, "overture_id": "1cf6de61-92eb-40c4-be28-513df0dcaf35", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7028858, 35.6592884 ], [ 139.702862399999987, 35.6593419 ], [ 139.702747100000011, 35.6593067 ], [ 139.702771399999989, 35.6592546 ], [ 139.7028858, 35.6592884 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 11, "overture_id": "21367bab-af93-4dca-bf9c-a65708051c5a", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7036803, 35.6585017 ], [ 139.7036139, 35.6584833 ], [ 139.7036697, 35.6583584 ], [ 139.703737100000012, 35.6583662 ], [ 139.7036803, 35.6585017 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 12, "overture_id": "236104c3-84a8-47c1-b872-9860b64a29fa", "name_primary": "サンクスビル", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7011947, 35.6613077 ], [ 139.70095950000001, 35.6613288 ], [ 139.7009519, 35.6612825 ], [ 139.701190200000013, 35.6612626 ], [ 139.7011947, 35.6613077 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 13, "overture_id": "23f68c5b-71c1-4e47-a535-4a092bd91854", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7001093, 35.6596517 ], [ 139.7000763, 35.6596481 ], [ 139.700078499999989, 35.6596343 ], [ 139.7000808, 35.6596207 ], [ 139.7001138, 35.6596242 ], [ 139.7001093, 35.6596517 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 14, "overture_id": "27112e19-5779-46d8-99d1-1a07c9c1d768", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7009482, 35.6591558 ], [ 139.7008016, 35.6591385 ], [ 139.7008258, 35.6590153 ], [ 139.7009717, 35.6590342 ], [ 139.7009521, 35.6591336 ], [ 139.7009482, 35.6591558 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 15, "overture_id": "28ada99a-21bf-42ff-b6d7-be0a0b26b3e3", "name_primary": "渋谷東日本ビル", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.700953199999987, 35.6612732 ], [ 139.7009495, 35.6611664 ], [ 139.701184, 35.6611653 ], [ 139.701194, 35.6612552 ], [ 139.700953199999987, 35.6612732 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 16, "overture_id": "29a2845a-cdc5-4aa7-ac7c-0ce136d7e197", "name_primary": "東京スクールオブミュージック専門学校渋谷第2校舎", "building_class": "college", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.703710699999988, 35.6611359 ], [ 139.7036003, 35.661165 ], [ 139.7036077, 35.6611834 ], [ 139.703522, 35.661206 ], [ 139.70347240000001, 35.6610819 ], [ 139.7036503, 35.6610932 ], [ 139.7036895, 35.6610829 ], [ 139.703710699999988, 35.6611359 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 17, "overture_id": "2c1a77ab-b6f8-4a1f-b4ba-b95e3fba63bf", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7033422, 35.6593349 ], [ 139.703213799999986, 35.6595646 ], [ 139.7031249, 35.6595511 ], [ 139.7031901, 35.6593654 ], [ 139.703211, 35.6593243 ], [ 139.7032376, 35.6593313 ], [ 139.7032552, 35.6593028 ], [ 139.7033422, 35.6593349 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 18, "overture_id": "2c40b108-7a5d-4e5a-9275-a464616b6e86", "name_primary": "", "building_class": "", "height": 30.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7028171, 35.6598877 ], [ 139.702736, 35.6598859 ], [ 139.702617, 35.6597873 ], [ 139.7027047, 35.6597141 ], [ 139.702826499999986, 35.6597316 ], [ 139.7028171, 35.6598877 ] ], [ [ 139.70271756, 35.659795692 ], [ 139.70271756, 35.659804372 ], [ 139.702728035, 35.659804372 ], [ 139.702728035, 35.659795692 ], [ 139.70271756, 35.659795692 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 19, "overture_id": "2c4b68ab-0162-4896-9404-f5df5b3479b6", "name_primary": "共栄ビル", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7037724, 35.6585288 ], [ 139.70369070000001, 35.658506 ], [ 139.7037541, 35.6583544 ], [ 139.70384, 35.6583634 ], [ 139.7037724, 35.6585288 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 20, "overture_id": "2c7a7e5e-8ef8-4734-a812-a1a36e709bf8", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7002922, 35.6618328 ], [ 139.700167099999987, 35.6618375 ], [ 139.700164300000012, 35.6617887 ], [ 139.7002895, 35.661784 ], [ 139.7002922, 35.6618328 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 21, "overture_id": "2e3d3476-bc60-4d92-a59a-4cb2aaeaa3d3", "name_primary": "MAGNET SHIBUYA", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.700983799999989, 35.6601217 ], [ 139.7008993, 35.6601363 ], [ 139.700782800000013, 35.6596766 ], [ 139.700860799999987, 35.6595937 ], [ 139.701050900000013, 35.6595858 ], [ 139.7010502, 35.6597431 ], [ 139.7010765, 35.6600607 ], [ 139.7009683, 35.660077 ], [ 139.700983799999989, 35.6601217 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 22, "overture_id": "2edf3b5a-c9a7-4d67-b57b-e6891fe8263b", "name_primary": "", "building_class": "roof", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.702233400000011, 35.6599842 ], [ 139.7022203, 35.6599837 ], [ 139.702226, 35.6598823 ], [ 139.7022519, 35.6598833 ], [ 139.7022461, 35.6599846 ], [ 139.702233400000011, 35.6599842 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 23, "overture_id": "2f539d97-f723-4927-8208-7bd614daf1e7", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7006873, 35.6609015 ], [ 139.7005119, 35.6609 ], [ 139.7005106, 35.6608351 ], [ 139.700693300000012, 35.6608285 ], [ 139.7006873, 35.6609015 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 24, "overture_id": "348d3001-94c6-42c7-90a3-8e15c681835e", "name_primary": "Zara", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.701175299999989, 35.6607462 ], [ 139.700943200000012, 35.6607253 ], [ 139.7009458, 35.6606009 ], [ 139.7011649, 35.6606144 ], [ 139.701175299999989, 35.6607462 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 25, "overture_id": "371f3842-eea7-4bd0-bf57-c568f5669832", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7002738, 35.6615817 ], [ 139.700080500000013, 35.6616881 ], [ 139.7000434, 35.6616598 ], [ 139.7002095, 35.6615347 ], [ 139.7002654, 35.6615447 ], [ 139.7002738, 35.6615817 ] ] ] } },
1:07
Write
solve.py
content · 54 lines · py
import geopandas as gpd
from shapely.ops import orient
from shapely.geometry import Polygon, MultiPolygon
def fix_geometry(geom):
if geom.is_empty:
return geom
# Use buffer(0) to fix any structural topological issues first
if not geom.is_valid:
geom = geom.buffer(0)
if geom.geom_type == 'Polygon':
# orient(geom, sign=1.0) ensures exterior is CCW and interior is CW
# This matches the RFC 7946 GeoJSON standard for the right-hand rule
return orient(geom, sign=1.0)
elif geom.geom_type == 'MultiPolygon':
new_parts = []
for part in geom.geoms:
if not part.is_empty:
new_parts.append(orient(part, sign=1.0))
return MultiPolygon(new_parts)
return geom
def solve():
input_file = 'tokyo_buildings_legacy.geojson'
output_file = 'tokyo_buildings_fixed.geojson'
print(f"Loading {input_file}...")
gdf = gpd.read_file(input_file)
print("Original validity stats:")
print(gdf.geometry.is_valid.value_counts())
# Apply the fix
print("Fixing geometries (winding and validity)...")
gdf['geometry'] = gdf['geometry'].apply(fix_geometry)
print("Final validity stats:")
print(gdf.geometry.is_valid.value_counts())
# Final check: ensure everything is still a polygon or multipolygon
# and that the number of features is the same.
print(f"Total features: {len(gdf)}")
print(f"Saving to {output_file}...")
gdf.to_file(output_file, driver='GeoJSON')
print("Done.")
if __name__ == "__main__":
solve()
Wrote solve.py (1620 chars).
1:10
Bash
python solve.py
Loading tokyo_buildings_legacy.geojson... Original validity stats: True 100 Name: count, dtype: int64 Fixing geometries (winding and validity)... Final validity stats: True 100 Name: count, dtype: int64 Total features: 100 Saving to tokyo_buildings_fixed.geojson... Done.
1:15
Bash
# Compare a feature with a hole in the original vs the fixed
# Compare a feature with a hole in the original vs the fixed # Feature 18 has a hole. grep -A 20 '"feature_id": 18' tokyo_buildings_legacy.geojson | head -n 40 grep -A 20 '"feature_id": 18' tokyo_buildings_fixed.geojson | head -n 40
"feature_id": 18,
"overture_id": "2c40b108-7a5d-4e5a-9275-a464616b6e86",
"name_primary": "",
"building_class": "",
"height": 30.0
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
139.7037724,
35.6585288
],
[
139.70384,
35.6583634
],
{ "type": "Feature", "properties": { "feature_id": 18, "overture_id": "2c40b108-7a5d-4e5a-9275-a464616b6e86", "name_primary": "", "building_class": "", "height": 30.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7028171, 35.6598877 ], [ 139.702736, 35.6598859 ], [ 139.702617, 35.6597873 ], [ 139.7027047, 35.6597141 ], [ 139.702826499999986, 35.6597316 ], [ 139.7028171, 35.6598877 ] ], [ [ 139.70271756, 35.659795692 ], [ 139.70271756, 35.659804372 ], [ 139.702728035, 35.659804372 ], [ 139.702728035, 35.659795692 ], [ 139.70271756, 35.659795692 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 19, "overture_id": "2c4b68ab-0162-4896-9404-f5df5b3479b6", "name_primary": "共栄ビル", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7037724, 35.6585288 ], [ 139.70369070000001, 35.658506 ], [ 139.7037541, 35.6583544 ], [ 139.70384, 35.6583634 ], [ 139.7037724, 35.6585288 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 20, "overture_id": "2c7a7e5e-8ef8-4734-a812-a1a36e709bf8", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7002922, 35.6618328 ], [ 139.700167099999987, 35.6618375 ], [ 139.700164300000012, 35.6617887 ], [ 139.7002895, 35.661784 ], [ 139.7002922, 35.6618328 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 21, "overture_id": "2e3d3476-bc60-4d92-a59a-4cb2aaeaa3d3", "name_primary": "MAGNET SHIBUYA", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.700983799999989, 35.6601217 ], [ 139.7008993, 35.6601363 ], [ 139.700782800000013, 35.6596766 ], [ 139.700860799999987, 35.6595937 ], [ 139.701050900000013, 35.6595858 ], [ 139.7010502, 35.6597431 ], [ 139.7010765, 35.6600607 ], [ 139.7009683, 35.660077 ], [ 139.700983799999989, 35.6601217 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 22, "overture_id": "2edf3b5a-c9a7-4d67-b57b-e6891fe8263b", "name_primary": "", "building_class": "roof", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.702233400000011, 35.6599842 ], [ 139.7022203, 35.6599837 ], [ 139.702226, 35.6598823 ], [ 139.7022519, 35.6598833 ], [ 139.7022461, 35.6599846 ], [ 139.702233400000011, 35.6599842 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 23, "overture_id": "2f539d97-f723-4927-8208-7bd614daf1e7", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7006873, 35.6609015 ], [ 139.7005119, 35.6609 ], [ 139.7005106, 35.6608351 ], [ 139.700693300000012, 35.6608285 ], [ 139.7006873, 35.6609015 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 24, "overture_id": "348d3001-94c6-42c7-90a3-8e15c681835e", "name_primary": "Zara", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.701175299999989, 35.6607462 ], [ 139.700943200000012, 35.6607253 ], [ 139.7009458, 35.6606009 ], [ 139.7011649, 35.6606144 ], [ 139.701175299999989, 35.6607462 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 25, "overture_id": "371f3842-eea7-4bd0-bf57-c568f5669832", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7002738, 35.6615817 ], [ 139.700080500000013, 35.6616881 ], [ 139.7000434, 35.6616598 ], [ 139.7002095, 35.6615347 ], [ 139.7002654, 35.6615447 ], [ 139.7002738, 35.6615817 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 26, "overture_id": "3c8b6f1b-d6b3-4f03-a4b5-c5508dcaea51", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.703455100000014, 35.660466 ], [ 139.7033433, 35.6604443 ], [ 139.703425399999986, 35.6601648 ], [ 139.7035372, 35.6601865 ], [ 139.703455100000014, 35.660466 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 27, "overture_id": "3ee49086-6ea2-4e7f-b99f-fc2edd7193ed", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.702717799999988, 35.6598821 ], [ 139.7025218, 35.6598754 ], [ 139.7025218, 35.6598616 ], [ 139.7026156, 35.659794 ], [ 139.702717799999988, 35.6598821 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 28, "overture_id": "3f3febaa-698d-497d-be85-c18945288b41", "name_primary": "", "building_class": "roof", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7024615, 35.6600032 ], [ 139.7024489, 35.6600028 ], [ 139.7024539, 35.6599073 ], [ 139.702481299999988, 35.6599082 ], [ 139.7024763, 35.6600037 ], [ 139.7024615, 35.6600032 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 29, "overture_id": "3f9e7083-4ec2-4669-bc45-7ae9fde983a4", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7015039, 35.6595559 ], [ 139.7015049, 35.6596361 ], [ 139.701505700000013, 35.6596938 ], [ 139.701507, 35.6597962 ], [ 139.7014692, 35.6597965 ], [ 139.701467799999989, 35.6596898 ], [ 139.701467400000013, 35.6596561 ], [ 139.7014661, 35.6595562 ], [ 139.7015039, 35.6595559 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 30, "overture_id": "3ff6f135-53ff-48d7-9be7-611b5a1926e1", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7026526, 35.6602298 ], [ 139.70249290000001, 35.6602205 ], [ 139.7024954, 35.6601476 ], [ 139.7026592, 35.6601536 ], [ 139.7026526, 35.6602298 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 31, "overture_id": "41e04ebc-5628-4614-983d-7d2bc049d0cd", "name_primary": "全国婦人会館・ちふれビル", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7034562, 35.6610918 ], [ 139.703512100000012, 35.6612091 ], [ 139.7033032, 35.6612748 ], [ 139.7032656, 35.6612751 ], [ 139.7032652, 35.6612298 ], [ 139.703280400000011, 35.6612297 ], [ 139.7032801, 35.6611929 ], [ 139.703305699999987, 35.6611928 ], [ 139.703304800000012, 35.6610927 ], [ 139.7034562, 35.6610918 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 32, "overture_id": "42569a2d-bc9c-47cf-b338-63e06079d81b", "name_primary": "宮益不動尊", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7035951, 35.6602969 ], [ 139.7035425, 35.6602858 ], [ 139.703554200000013, 35.6602492 ], [ 139.703606799999989, 35.6602603 ], [ 139.7035951, 35.6602969 ] ], [ [ 139.703573364, 35.660271142 ], [ 139.703573364, 35.660273527 ], [ 139.703576578999986, 35.660273527 ], [ 139.703576578999986, 35.660271142 ], [ 139.703573364, 35.660271142 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 33, "overture_id": "42a04eb1-81e4-432c-aba7-f701afb37e1b", "name_primary": "Doctor's Building", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7021437, 35.6608561 ], [ 139.70195240000001, 35.6608552 ], [ 139.701938399999989, 35.6608079 ], [ 139.7021431, 35.6608087 ], [ 139.7021437, 35.6608561 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 34, "overture_id": "45eee2cf-762c-4069-ad33-15ffc6a532fd", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.70351, 35.6583273 ], [ 139.703433499999988, 35.6584926 ], [ 139.7034042, 35.6584846 ], [ 139.7034751, 35.658326 ], [ 139.70351, 35.6583273 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 35, "overture_id": "4615c1ba-4f89-423b-8715-4f89303192c9", "name_primary": "PMO渋谷", "building_class": "office", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7035291, 35.6614183 ], [ 139.703483299999988, 35.6613241 ], [ 139.7034247, 35.6613429 ], [ 139.7033898, 35.6612712 ], [ 139.703615899999988, 35.6611986 ], [ 139.7036966, 35.6613646 ], [ 139.7035291, 35.6614183 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 36, "overture_id": "4642244a-76bc-4f73-9b50-9621e554966b", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.70379650000001, 35.6598824 ], [ 139.703649, 35.6601406 ], [ 139.703585299999986, 35.6601161 ], [ 139.7037276, 35.6598679 ], [ 139.70379650000001, 35.6598824 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 37, "overture_id": "4a232708-e5bc-48da-bfba-70db0a054f1f", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.703694, 35.6586863 ], [ 139.703545600000012, 35.6586423 ], [ 139.703611099999989, 35.6584963 ], [ 139.7037595, 35.6585402 ], [ 139.703694, 35.6586863 ] ] ] } },
{ "type": "Feature", "properties": { "feature_id": 38, "overture_id": "4a869c37-9dae-4491-b2da-57ebd037fa34", "name_primary": "", "building_class": "", "height": null }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 139.7029398, 35.660622 ], [ 139.702872500000012, 35.6606192 ], [ 139.7028827, 35.6604838 ], [ 139.70299940000001, 35.66049 ], [ 139.702975400000014, 35.6606254 ], [ 139.702959099999987, 35.6607108 ], [ 139.702923700000014, 35.6607071 ], [ 139.7029398, 35.660622 ] ] ] } },
1:39
Bash
# Let's look at the coordinates of the exterior vs interior for feature 18 in th...
# Let's look at the coordinates of the exterior vs interior for feature 18 in the original file.
# I'll use python to get the exact ring directions from the original file.
cat << 'EOF' > check_winding.py
import json
from shapely.geometry import shape
with open('tokyo_buildings_legacy.geojson') as f:
data = json.load(f)
for feat in data['features']:
if feat['properties']['feature_id'] == 18:
geom = shape(feat['geometry'])
print("Feature 18 (Original):")
print(f"Exterior: {geom.exterior.is_ccw}")
for i, interior in enumerate(geom.interiors):
print(f"Interior {i}: {interior.is_ccw}")
break
for feat in data['features']:
if feat['properties']['feature_id'] == 18:
# Re-reading from fixed file to be sure
pass
EOF
# Wait, I need to read the fixed file too.
cat << 'EOF' > check_winding.py
import json
from shapely.geometry import shape
def check_file(filename):
with open(filename) as f:
data = json.load(f)
for feat in data['features']:
if feat['properties']['feature_id'] == 18:
geom = shape(feat['geometry'])
print(f"{filename} - Feature 18:")
print(f" Exterior: {geom.exterior.is_ccw}")
for i, interior in enumerate(geom.interiors):
print(f" Interior {i}: {interior.is_ccw}")
return
check_file('tokyo_buildings_legacy.geojson')
check_file('tokyo_buildings_fixed.geojson')
EOF
python check_winding.py
tokyo_buildings_legacy.geojson - Feature 18: Exterior: False Interior 0: True tokyo_buildings_fixed.geojson - Feature 18: Exterior: True Interior 0: False
2:14