logoSpatial Topology

opening

製作 opening.geojson

製作 opening.geojson 需要從 JOSM 開始,通常這時候已經做完 level.geojson & unit.geojson 了,因此可以直接在 JOSM 中開啟這些檔案,並且在 JOSM 中繪製出入口的位置。

在 JOSM 中繪製出入口

在 JOSM 中要把所有出入口的位置標示出來,以線段的方式繪製 (快捷鍵: A),並且如果該樓層有電梯、樓梯、電扶梯等設施,也要確保這些設施的出入口都與人行區域之間有出入口標示。如果繪製的樓層是該建築物的一樓,別需繪製與室外連接的出入口,這些出入口的類別必須標示成 pedestrian.principle (IMDF Sandbox 中顯示 pedestrian principle)。

{
  "type": "Feature",
  "id": "5b8e0a35-0fbf-4ae6-aed0-fd985b7aea37",
  "feature_type": "opening",
  "properties": {
    "category": "pedestrian", 
    "accessibility": null,
    "access_control": null,
    "door": null,
    "name": null,
    "alt_name": null,
    "display_point": {
      "type": "Point",
      "coordinates": [-123.4304217, 48.6405852]
    },
    "level_id": "2c4739db-c23c-414d-bdd8-ae54dc97c02f"
  },
  "geometry": {
    "type": "LineString",
    "coordinates": [
      [-123.4304155, 48.6405846],
      [-123.4304278, 48.6405859]
    ]
  }
}
{
  "type": "Feature",
  "id": "661da5e3-d419-4749-8133-f75c9ac01e86",
  "feature_type": "opening",
  "properties": {
    "category": "pedestrian.principal", 
    "accessibility": null,
    "access_control": null,
    "door": null,
    "name": null,
    "alt_name": null,
    "display_point": {
      "type": "Point",
      "coordinates": [121.5156311, 25.1227732]
    },
    "level_id": "bdc3f53d-85b3-49b7-a8a5-59db2433dbf8"
  },
  "geometry": {
    "type": "LineString",
    "coordinates": [
      [121.5156214, 25.1227727],
      [121.5156407, 25.1227736]
    ]
  }
}

opening-josm

匯出 opening.osm 並轉換成 geojson

匯出 opening.osm 後,使用 osmtogeojson 這個線上工具將 opening.osm 轉換成 geojson。

<?xml version='1.0' encoding='UTF-8'?>
<osm version='0.6' generator='JOSM'>
  <node id='-31738' action='modify' visible='true' lat='25.12279073139' lon='121.51612223234' />
  <node id='-31739' action='modify' visible='true' lat='25.12280323507' lon='121.51612150306' />
  <node id='-31740' action='modify' visible='true' lat='25.12281545215' lon='121.51612080458' />
  <node id='-31741' action='modify' visible='true' lat='25.12282450884' lon='121.51612027701' />
  <node id='-31742' action='modify' visible='true' lat='25.12282772279' lon='121.51612008912' />
  <node id='-31743' action='modify' visible='true' lat='25.12282818954' lon='121.51613001282' />
  <node id='-31744' action='modify' visible='true' lat='25.12279095399' lon='121.51613828915' />
  <node id='-31745' action='modify' visible='true' lat='25.12279187767' lon='121.51615778173' />
  <node id='-31746' action='modify' visible='true' lat='25.12276391235' lon='121.51594899981' />
  <node id='-31747' action='modify' visible='true' lat='25.12276335809' lon='121.51593732686' />
  <node id='-31748' action='modify' visible='true' lat='25.12276450008' lon='121.5159614389' />
  <node id='-31750' action='modify' visible='true' lat='25.12276531651' lon='121.51597857746' />
  <node id='-31751' action='modify' visible='true' lat='25.12276581238' lon='121.51598899331' />
  <node id='-31752' action='modify' visible='true' lat='25.12276814011' lon='121.51603803314' />
  <node id='-31753' action='modify' visible='true' lat='25.12276765862' lon='121.51602788319' />
  <node id='-31754' action='modify' visible='true' lat='25.1227686114' lon='121.51604794655' />
  <node id='-31755' action='modify' visible='true' lat='25.12277044433' lon='121.51608653711' />
  <node id='-31756' action='modify' visible='true' lat='25.12277095395' lon='121.51609728673' />
  <node id='-31757' action='modify' visible='true' lat='25.12277149723' lon='121.51610870028' />
  <node id='-31758' action='modify' visible='true' lat='25.12277387731' lon='121.51615882428' />
  <node id='-31759' action='modify' visible='true' lat='25.12277333506' lon='121.51614739615' />
  <node id='-31760' action='modify' visible='true' lat='25.12278876908' lon='121.51592018112' />
  <node id='-31761' action='modify' visible='true' lat='25.12278789951' lon='121.51590184175' />
  <node id='-31763' action='modify' visible='true' lat='25.12278408894' lon='121.51586136651' />
  <node id='-31764' action='modify' visible='true' lat='25.12278493156' lon='121.51587908279' />
  <node id='-31765' action='modify' visible='true' lat='25.1227825115' lon='121.51582816437' />
  <node id='-31766' action='modify' visible='true' lat='25.12278344395' lon='121.51584777966' />
  <node id='-31767' action='modify' visible='true' lat='25.12278243963' lon='121.51582660665' />
  <node id='-31768' action='modify' visible='true' lat='25.12279629666' lon='121.51582580531' />
  <node id='-31769' action='modify' visible='true' lat='25.12272220898' lon='121.5157290216' />
  <node id='-31770' action='modify' visible='true' lat='25.12272279289' lon='121.51574129382' />
  <node id='-31771' action='modify' visible='true' lat='25.1227235924' lon='121.51575812705' />
  <node id='-31772' action='modify' visible='true' lat='25.12272433662' lon='121.51577379276' />
  <node id='-31773' action='modify' visible='true' lat='25.12272511546' lon='121.51579019341' />
  <node id='-31774' action='modify' visible='true' lat='25.1227259087' lon='121.51580689204' />
  <way id='-6952' action='modify' visible='true'>
    <nd ref='-31738' />
    <nd ref='-31739' />
  </way>
  <way id='-6953' action='modify' visible='true'>
    <nd ref='-31740' />
    <nd ref='-31741' />
  </way>
  <way id='-6954' action='modify' visible='true'>
    <nd ref='-31742' />
    <nd ref='-31743' />
  </way>
  <way id='-6955' action='modify' visible='true'>
    <nd ref='-31744' />
    <nd ref='-31745' />
  </way>
  <way id='-6956' action='modify' visible='true'>
    <nd ref='-31748' />
    <nd ref='-31746' />
    <nd ref='-31747' />
  </way>
  <way id='-6958' action='modify' visible='true'>
    <nd ref='-31750' />
    <nd ref='-31751' />
  </way>
  <way id='-6959' action='modify' visible='true'>
    <nd ref='-31754' />
    <nd ref='-31752' />
    <nd ref='-31753' />
  </way>
  <way id='-6961' action='modify' visible='true'>
    <nd ref='-31755' />
    <nd ref='-31756' />
    <nd ref='-31757' />
  </way>
  <way id='-6963' action='modify' visible='true'>
    <nd ref='-31758' />
    <nd ref='-31759' />
  </way>
  <way id='-6964' action='modify' visible='true'>
    <nd ref='-31760' />
    <nd ref='-31761' />
  </way>
  <way id='-6965' action='modify' visible='true'>
    <nd ref='-31763' />
    <nd ref='-31764' />
  </way>
  <way id='-6966' action='modify' visible='true'>
    <nd ref='-31765' />
    <nd ref='-31766' />
  </way>
  <way id='-6967' action='modify' visible='true'>
    <nd ref='-31767' />
    <nd ref='-31768' />
  </way>
  <way id='-6968' action='modify' visible='true'>
    <nd ref='-31769' />
    <nd ref='-31770' />
  </way>
  <way id='-6969' action='modify' visible='true'>
    <nd ref='-31771' />
    <nd ref='-31772' />
  </way>
  <way id='-6970' action='modify' visible='true'>
    <nd ref='-31773' />
    <nd ref='-31774' />
  </way>
</osm>
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "id": "way/-6952",
      "properties": {
        "id": "way/-6952"
      },
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [121.51612223234, 25.12279073139],
          [121.51612150306, 25.12280323507]
        ]
      }
    },
    {
      "type": "Feature",
      "id": "way/-6953", 
      "properties": {
        "id": "way/-6953"
      },
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [121.51612080458, 25.12281545215],
          [121.51612027701, 25.12282450884]
        ]
      }
    }
    // ...
  ]
}

轉換為 IMDF 格式

再來將轉換後的 geojson 檔案透過 ChatGPT 的幫助轉換成 IMDF 格式,這裡的轉換方式與 level.geojson & unit.geojson 是一樣的。

使用 ChatGPT o1-preview/o1-mini 進行轉換

這邊將 nameid 改為一樣是為了在 geojson.io 上可以看到名稱,要注意,然後 display_point 的座標是兩點之間的中心點。

幫我兩兩座標組成一條線,根據我提供的描述有些線段包含三個點,也請你拆成兩個線段,這樣你應該知道中間的那個點會被用在兩個不同的線段,這些用以表示房間開口,並將格式改成以下的格式,
level_id改成"2c4739db-c23c-414d-bdd8-ae54dc97c02f",id改成line 1, line 2...,name改成跟id一樣,
display_point的座標就用兩點之間的中心點的座標,完整輸出一份geojson描述給我。
{
      "type": "Feature",
      "id": "5b8e0a35-0fbf-4ae6-aed0-fd985b7aea37",
      "feature_type": "opening",
      "properties": {
        "category": "pedestrian",
        "accessibility": null,
        "access_control": null,
        "door": null,
        "name": null,
        "alt_name": null,
        "display_point": {
          "type": "Point",
          "coordinates": [-123.4304217, 48.6405852]
        },
        "level_id": "2c4739db-c23c-414d-bdd8-ae54dc97c02f"
      },
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [-123.4304155, 48.6405846],
          [-123.4304278, 48.6405859]
        ]
      }
    }
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "id": "ef320e28-4013-4b68-ae8e-c056699ad0a4",
      "feature_type": "opening",
      "properties": {
        "category": "pedestrian",
        "accessibility": null,
        "access_control": null,
        "door": null,
        "name": null,
        "alt_name": null,
        "display_point": {
          "type": "Point",
          "coordinates": [121.5161219, 25.1227975]
        },
        "level_id": "2a59caed-8774-459a-8c4a-9b344530f52d"
      },
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [121.5161222, 25.1227907],
          [121.5161218, 25.1227985],
          [121.5161218, 25.1227987],
          [121.5161215, 25.1228032]
        ]
      }
    },
    {
      "type": "Feature",
      "id": "84f5d736-9fe4-457a-aee9-104627881394",
      "feature_type": "opening",
      "properties": {
        "category": "pedestrian",
        "accessibility": null,
        "access_control": null,
        "door": null,
        "name": null,
        "alt_name": null,
        "display_point": {
          "type": "Point",
          "coordinates": [121.5161205, 25.12282]
        },
        "level_id": "2a59caed-8774-459a-8c4a-9b344530f52d"
      },
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [121.5161208, 25.1228155],
          [121.5161203, 25.1228245]
        ]
      }
    },
    // ...
  ]
}

name 要改成 null 或是字串

確認完轉換後的 geojson 檔案後,要把 name 改為 null 或是不重複的字串(只包含英文),不然審核在這步會沒法通過。

使用python轉換格式

opening 格式轉換.py
import json

# 輸入的 GeoJSON 資料(您提供的原始資料)
input_geojson = # 貼上 osm 轉換成的 geojson

# 輸出的 GeoJSON 模板
output_geojson = {
  "type": "FeatureCollection",
  "features": []
}

# 目標 level_id
level_id = "" # 填入目標 level_id

# 遍歷輸入資料中的每個特徵
for idx, feature in enumerate(input_geojson["features"]):
  geometry = feature["geometry"]
  coordinates = geometry["coordinates"]

# 只處理 LineString 類型的特徵
if geometry["type"] == "LineString" and len(coordinates) == 2:
  # 計算兩點的中心點座標
  midpoint = [
    (coordinates[0][0] + coordinates[1][0]) / 2,
    (coordinates[0][1] + coordinates[1][1]) / 2
  ]

# 建立新的特徵
new_feature = {
  "type": "Feature",
  "id": "", # 留空進 Sandbox 會自動生成
  "feature_type": "opening",
  "properties": {
    "category": "pedestrian",
    "accessibility": None,
    "access_control": None,
    "door": None,
    "name": None,
    "alt_name": None,
    "display_point": {
      "type": "Point",
      "coordinates": midpoint
    },
    "level_id": level_id
  },
  "geometry": geometry  # 保留原始的幾何資訊
}

# 將新的特徵添加到輸出資料中
output_geojson["features"].append(new_feature)

# 將結果轉換為 JSON 字串(可選)
output_json_str = json.dumps(output_geojson, ensure_ascii=False, indent=2)

# 輸出結果
print(output_json_str)
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "id": "way/-24362",
      "properties": {
        "id": "way/-24362"
      },
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [
            121.51378560222,
            25.12063446756
          ],
          [
            121.51378516298,
            25.12064269802
          ]
        ]
      }
    },
    {
      "type": "Feature",
      "id": "way/-24364",
      "properties": {
        "id": "way/-24364"
      },
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [
            121.51378749071,
            25.12059908138
          ],
          [
            121.51378662046,
            25.12061538795
          ]
        ]
      }
    },
    # ...
  ]
}
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "id": "",
      "feature_type": "opening",
      "properties": {
        "category": "pedestrian",
        "accessibility": null,
        "access_control": null,
        "door": null,
        "name": null,
        "alt_name": null,
        "display_point": {
          "type": "Point",
          "coordinates": [
            121.51378538259999,
            25.12063858279
          ]
        },
        "level_id": "90edf64c-eb50-472c-9417-56d804f4d174"
      },
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [121.51378560222, 25.12063446756],
          [121.51378516298, 25.12064269802]
        ]
      }
    },
    {
      "type": "Feature",
      "id": "",
      "feature_type": "opening",
      "properties": {
        "category": "pedestrian",
        "accessibility": null,
        "access_control": null,
        "door": null,
        "name": null,
        "alt_name": null,
        "display_point": {
          "type": "Point",
          "coordinates": [
            121.513787055585,
            25.120607234665002
          ]
        },
        "level_id": "90edf64c-eb50-472c-9417-56d804f4d174"
      },
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [121.51378749071, 25.12059908138],
          [121.51378662046, 25.12061538795]
        ]
      }
    },
    # ...
  ]
}