GeoJSONは、点、線、面などの位置情報を扱うときに使いやすいデータ形式です。現地で取得した座標を地図上に表示したい場合、測量結果や点検記録を位置と一緒に管理したい場合、あるいは別のシステムへ地物データを渡したい場合に、実務でよく使われます。一方で、GeoJSONは見た目がシンプルな反面、座標の順序、階層構造、プロパティの持たせ方、ファイル保存時の文字コードなどを間違えると、表示できない、位置がずれる、面が閉じない、属性が読めないといった問題が起きやすい形式でもあります。
この記事では、PythonでGeoJSONを作成する基本コードを確認しながら、実務で見落としやすい確認手順を5つに整理します。専門的な開発環境を前提にするのではなく、座標データを安全に作成し、受け渡し前に最低限チェックしたい担当者向けに説明します。
目次
• GeoJSONをPythonで作成する前に押さえる基本
• FeatureCollectionの形でGeoJSONを組み立てる
• Point、LineString、PolygonをPythonで作成する基本コード
• 確認手順1としてtypeと階層構造を確認する
• 確認手順2として座標順序と数値型を確認する
• 確認手順3としてPolygonの閉じ方と座標配列を確認する
• 確認手順4としてpropertiesの項目名と値を確認する
• 確認手順5として保存後の文字コードと読み戻しを確認する
• 実務でGeoJSONを扱うときの注意点
• まとめ
GeoJSONをPythonで作成する前に押さえる基本
GeoJSONは、位置情報をJSON形式で表現するためのデータ構造です。基本になる考え方は、地物の形をgeometryに入れ、地物に関する情報をpropertiesに入れることです。たとえば、点検箇所の位置であればgeometryには点の座標を入れ、propertiesには点検番号、名称、状態、記録日などを入れます。道路中心線のような線であればgeometryには複数の座標を順番に並べ、propertiesには路線名や管理区分などを 入れます。
PythonでGeoJSONを作成する場合、特別な外部ソフトウェアを使わなくても、辞書とリストを組み合わせることで基本的な構造を作れます。GeoJSONはJSONの構造に従うため、Pythonではdictとlistで組み立て、最後にjson形式として保存する流れが基本です。複雑な処理を行う場合は専用のライブラリを使うこともありますが、まずは標準的なデータ構造を理解しておくことが大切です。
GeoJSONの代表的なgeometryには、Point、LineString、Polygonがあります。Pointは単独の点、LineStringは複数点をつないだ線、Polygonは閉じた面を表します。実務では、現地写真の撮影位置、設備の位置、測点、点検対象箇所などはPointで扱うことが多く、道路縁、河川線形、施工範囲の境界線などはLineStringで扱えます。敷地範囲、補修範囲、調査範囲、区域境界などはPolygonとして扱います。
GeoJSONで特に注意したいのは、座標の順序です。一般的な緯度経度の会話では「緯度、経度」と言うことが多いですが、GeoJSONでは座標配列を「経度、緯度」の順に書くのが基本です。高さを含める場合は「経度、緯度、高さ」の順になります。この順序を逆にすると、国内の座標であってもまったく別の位置に表示される可能性があります。
また、GeoJSONはあくまでデータ形式であり、正しく作成しただけで業務上の意味が保証されるわけではありません。座標の取得方法、使用している座標参照系、桁数、現地との対応、属性情報の定義、ファイルの受け渡し条件などを合わせて確認する必要があります。Pythonで自動生成できるようにすると効率化できますが、最初に構造と確認項目を決めておかないと、同じようなミスを大量に作ってしまうことがあります。
FeatureCollectionの形でGeoJSONを組み立てる
複数の地物をまとめて扱う場合、GeoJSONではFeatureCollectionの形にするのが扱いやすいです。FeatureCollectionは、複数のFeatureをfeaturesの中に並べる構造です。Featureは、geometryとpropertiesを持つ単位です。点を1つだけ扱う場合でも、将来的に複数件へ増える可能性があるなら、最初からFeatureCollectionで作っておくと管理しやすくなります。
Pythonで基本的なFeatureCollectionを作ると、次のようになります。
`python import json
geojson_data = { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [139.767125, 35.681236] }, "properties": { "id": "P001", "name": "sample point", "status": "checked" } } ] }
with open("sample.geojson", "w", encoding="utf-8") as file: json.dump(geojson_data, file, ensure_ascii=False, indent=2) `
このコー ドでは、geojson_dataという辞書にGeoJSONの構造をそのまま表現しています。最上位のtypeはFeatureCollectionで、その中のfeaturesにFeatureを入れています。Featureの中にはgeometryとpropertiesがあり、geometryにはPointの座標、propertiesには任意の属性情報を入れています。最後にjson.dumpでファイルへ保存しています。
ensure_ascii=Falseを指定しているのは、日本語を含む属性を人が読みやすい形で保存しやすくするためです。これを指定しない場合でもJSONデータとして壊れるわけではありませんが、日本語がエスケープされた表記になり、目視確認しにくくなる場合があります。indent=2は、保存されたファイルを読みやすく整形するための指定です。機械処理だけで使うなら必須ではありませんが、実務ではファイルを開いて目視確認することも多いため、最初は整形して保存するほうが確認しやすくなります。
GeoJSONをPythonで作るときは、まず手書きに近い形で小さなデータを作り、正しく読めることを確認してから、表形式のデータや現地記録から自動生成する処理へ広げるのがよい進め方です。いきなり大量データを作ると、座標順序や属性名のミスに気づくまで時間がかかります。最初の1件、次に数件、最後に全件という段階で確認すると、問題の切り 分けがしやすくなります。
Point、LineString、PolygonをPythonで作成する基本コード
Pointは、単独の座標を扱うもっとも単純なgeometryです。点検箇所や写真位置のように、1つの位置を示すときに向いています。Pointのcoordinatesには、経度と緯度を1つの配列として入れます。
`python point_feature = { "type": "Feature", "geometry": { "type": "Point", "coordinates": [139.767125, 35.681236] }, "properties": { "id": "P001", "category": "inspection", "memo": "sample" } } `
LineStringは、複数の座標を順番につないだ線です。coordinatesには、座標配列をさらに配列として並べます。線の向きに意味がある場合は、座標の並び順も重要です。たとえば、起点から終点へ向かう線として扱う場合は、現場や図面上の定義と同じ順番で座標を並べます。
`python line_feature = { "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [139.767125, 35.681236], [139.768000, 35.681500], [139.769000, 35.681900] ] }, "properties": { "id": "L001", "category": "route", "memo": "sample line" } } `
Polygonは、閉じた面を扱います。coordinatesは、面の外周を表す座標列をさらに一段深い配列に入れる形になります。外周の最初の座標と最後の座標は同じにします。これにより、面が閉じた形として扱われます。穴のある面を表す場合は追加のリングを持たせることもできますが、基本ではまず外周だけを正しく作ることが重要です。
`python polygon_feature = { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [139.767000, 35.681000], [139.768000, 35.681000], [139.768000, 35.682000], [139.767000, 35.682000], [139.767000, 35.681000] ] ] }, "properties": { "id": "A001", "category": "area", "memo": "sample polygon" } } `
これらをFeatureCollectionとしてまとめる場合は、featuresの中に各Featureを並べます。地物の種類が混在していても、Featureごとにgeometryのtypeを分ければ、同じFeatureCollection内で扱えます。
`python import json
features = [ point_feature, line_feature, polygon_feature ]
geojson_data = { "type": "FeatureCollection", "features": features }
with open("mixed_sample.geojson", "w", encoding="utf-8") as file: json.dump(geojson_data, file, ensure_ascii=False, indent=2) `
実務では、これらのFeatureを手で1件ずつ書くことは少なく、表形式の記録や計測結果を読み取り、行ごとにFeatureを作成する処理にすることが多いです。ただし、自動化する前に、Point、LineString、Polygonの配列の深さがそれぞれ異なることを理解しておく必要があります。Pointは座標そのもの、LineStringは座標の並び、Polygonは座標の並びをリングとしてさらに包んだ形です。この違いを曖昧にしたまま作ると、見た目はJSONでもGeoJSONとして正しく解釈されない場合があります。
確認手順1としてtypeと階層構造を確認する
GeoJSONをPythonで作成したら、最初に確認したいのはtypeと階層構造です。GeoJSONは文字列としては自由に書けてしまうため、括弧の対応が合っていても、typeの位置やgeometryの階層が間違っていると、地物として読めないことがあります。特に、FeatureCollection、Feature、geometry、propertiesの関係が正しいかを確認することが大切です。
最上位をFeatureCollectionにする場合、typeはFeatureCollectionで、featuresは配列になります。そのfeaturesの中にFeatureが並び、各FeatureのtypeはFeatureです。geometryはFeatureの直下に置き、geometryの中にtypeとcoordinatesを入れます。propertiesもFeatureの直下に置きます。propertiesの中には、業務で使いたい属性を自由に入れられます。
よくあるミスは、geometryの外にcoordinatesを書いてしまうこと、propertiesをfeaturesと同じ階層に置いてしまうこと、Featureのtypeを入れ忘れること、FeatureCollectionのfeaturesを辞書にしてしまうことです。これらは、Pythonの辞書としては成立する場合がありますが、GeoJSONとして期待通りに扱えない原因になります。
確認用に、最低限の構造を検査する簡単な関数を用意しておくと便利です。厳密な検証をすべて行うものではありませんが、基本的な階層ミスを早い段階で見つける助けになります。
`python def check_basic_structure(data): if data.get("type") != "FeatureCollection": raise ValueError("top level type must be FeatureCollection")
features = data.get("features")
if not isinstance(features, list):
raise ValueError("features must be a list")
for index, feature in enumerate(features):
if feature.get("type") != "Feature":
raise ValueError(f"feature {index} type must be Feature")
if "geometry" not in feature:
raise ValueError(f"feature {index} has no geometry")
if "properties" not in feature:
raise ValueError(f"feature {index} has no properties")
geometry = feature["geometry"]
if not isinstance(geometry, dict):
raise ValueError(f"feature {index} geometry must be an object")
if "type" not in geometry or "coordinates" not in geometry:
raise ValueError(f"feature {index} geometry is incomplete")
return True`
このような簡易チェックは、作成処理の最後に実行しておくと有効です。作成した直後にエラーを出せれば、どの段階で構造が崩れたのかを追いやすくなります。大量の地物を扱う場合は、何番目のFeatureで問題が起きたかをメッセージに含めておくと、元データの行番号や管理番号と照合しやすくなります。
確認手順2として座標順序と数値型を確認する
次に確認するのは、座標の順序と数値型です。GeoJSONのcoordinatesでは、経度、緯度の順で座標を書きます。高さを入れる場合は、その後に高さを続けます。実務で扱う元データは、緯度、経度の順で記録されていることも多いため、Pythonで変換するときに入れ替えを忘れないようにする必要があります。
座標順序のミスは、構文エラーにならない点が厄介です。たとえば、 日本国内の緯度と経度を逆にしても、どちらも数値としては成立します。そのため、ファイルとしては保存でき、構文としても読めるのに、地図上では別の場所に表示されることがあります。GeoJSONを作成する処理では、変数名をlat、lonのように明確にし、coordinatesへ入れる瞬間にlon、latの順で並べるようにします。
`python records = [ { "id": "P001", "name": "point 1", "lat": 35.681236, "lon": 139.767125 }, { "id": "P002", "name": "point 2", "lat": 35.682000, "lon": 139.768000 } ]
features = []
for record in records: lat = float(record["lat"]) lon = float(record["lon"])
feature = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [lon, lat]
},
"properties": {
"id": record["id"],
"name": record["name"]
}
}
features.append(feature)`
座標は文字列ではなく数値として保存するのが基本です。元データが文字列で入っている場合は、floatで数値化してからcoordinatesへ入れると、後続処理で扱いやすくなります。ただし、空欄や不要な文字が混じっていると変換時にエラーになります。その場合は、無理に0へ置き換えるのではなく、元データに問題があるものとして記録し、確認対象にするほうが安全です。
座標範囲の確認も重要です。経度は東西方向、緯度は南北方向を示します。経度と緯度には取り得る範囲がありますが、実務では世界全体の範囲だけでなく、対象地域の範囲で確認することが大切です。たとえば、ある現場の周辺だけを扱うなら、現場の中心座標から極端に離れた値が入っていないかを見るだけでも、桁落ち、符号ミス、座標順序の逆転を見つけやすくなります。
確認手順3としてPolygonの閉じ方と座標配列を確認する
Polygonを作成するときは、外周の最初の座標と最後の座標を同じにする必要があります。これは、面を閉じた形として表すためです。最後の点が抜けていると、表示ソフトによっては閉じて見える場合があっても、データとしては適切ではありません。特に、後続処理で面積計算、範囲判定、重なり確認などを行う場合は、閉じたリングになっているかを必ず確認します。
PythonでPolygonを作るときは、座標列を受け取ったあと、先頭と末尾を比較し、異なる場合は先頭座標を末尾に追加する処理を入れておくと確認しやすくなります。あわせて、閉じた後のリングが4点以上になることも確認します。これは、三角形のような最小の面でも、先頭と末尾を同じ座標にするため、座標配列としては4つ以上の位置が必要になるためです。
`python def close_ring(coordinates): if len(coordinates) < 3: raise ValueError("polygon ring needs at least three distinct points before closing")
closed = [list(position) for position in coordinates]
if closed[0] != closed[-1]:
closed.append(closed[0])
if len(closed) < 4:
raise ValueError("closed polygon ring needs at least four positions")
return closedring = [ [139.767000, 35.681000], [139.768000, 35.681000], [139.768000, 35.682000], [139.767000, 35.682000] ]
closed_ring = close_ring(ring)
polygon_feature = { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [closed_ring] }, "properties": { "id": "A001", "name": "area 1" } } `
ここで注意したいのは、Polygonのcoordinatesは、単なる座標列ではなく、リングの配列になっていることです。外周だけのPolygonでも、coordinatesには[closed_ring]のよう に一段包んだ形で入れます。この一段を忘れると、配列の深さがLineStringと同じになり、Polygonとして正しく扱えない原因になります。
また、Polygonの座標は、図面や現地での境界順序と対応しているかも確認が必要です。座標の順番が飛んでいると、面が交差したり、意図しない形として解釈されたりすることがあります。特に、表形式の点群から面を作る場合、単に上から順に並べれば正しい面になるとは限りません。境界をたどる順番で座標を並べる必要があります。
面の内側に穴を持たせる場合は、外周リングの後に内側リングを追加します。ただし、実務で初めてGeoJSONを作る段階では、穴ありPolygonまで一度に扱おうとすると確認項目が増えます。まずは外周だけのPolygonを正しく作成し、閉じ方、配列の深さ、点の順序を確認できるようにしてから、必要に応じて複雑な形へ広げるのが安全です。
確認手順4としてpropertiesの項目名と値を確認する
GeoJSONでは、geometryが位置や形を表し、propertiesが属性情報を表します。実務で使うGeoJSONは、座標だけでなく、管理番号、分類、名称、点検結果、施工区分、記録日、担当区分などの属性が重要になることが多いです。位置が正しくても、propertiesの項目名がばらばらだったり、値の型が混在していたりすると、後工程で検索、分類、集計、表示を行うときに問題になります。
たとえば、同じ意味の項目にid、ID、Id、管理番号のような複数の名前が混在すると、読み込み側で別々の項目として扱われる可能性があります。Pythonで作成する段階で、propertiesに入れる項目名を固定し、元データの列名が違っていても出力時には統一することが大切です。
`python def create_point_feature(record): lat = float(record["lat"]) lon = float(record["lon"])
return {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [lon, lat]
},
"properties": {
"id": str(record["id"]),
"name": str(record.get("name", "")),
"category": str(record.get("category", "")),
"status": str(record.get("status", ""))
}
}`
この例では、propertiesの項目をid、name、category、statusに固定しています。record側に値がない場合でも、空文字を入れることで項目の有無をそろえています。ただし、空文字でよいか、未記入を別の表現にするかは、業務上のルールに合わせて決める必要があります。未確認、対象外、空欄が同じ意味ではない場合は、単純に空文字へまとめると後で判断できなくなります。
日付や数値の扱いにも注意が必要です。JSONとしては文字列、数値、真偽値などを入れられますが、受け渡し先でどのように読まれるかは事前に確認しておく必要があります。点検日を文字列で入れる場合は、表記ゆれを防ぐために形式を統一します。数量や延長を数値として入れる場合は、単位を項目名や別項目で分かるようにしておくと誤解を減らせます。
propertiesには何でも入れられるため、つい元データの項目をすべて入れたくなります。しかし、不要な項目や確認されていない項目を大量に入れると、データの意味が分かりにくくなります。外部へ渡すGeoJSONでは、利用目的に必要な項目を選び、項目名、値の型、空欄時の扱いを決めてから出力することが重要です。
確認手順5として保存後の文字コードと読み戻しを確認する
PythonでGeoJSONを作成したら、保存できた時点で終わりにせず、保存したファイルをもう一度読み戻して確認することが大切です。作成時の辞書データでは問題がなくても、保存時の文字コード、改行、ファイル名、出力先の指定などで問題が起きる場合があります。特に日本語のpropertiesを含む場合は、encodingを明示して保存し、読み戻し時にも同じ文字コードで開くと確認しやすくなります。
`python with open("sample.geojson", "w", encoding="utf-8") as file: json.dump(geojson_data, file, ensure_ascii=False, indent=2)
with open("sample.geojson", "r", encoding="utf-8") as file: loaded_data = json.load(file)
check_basic_structure(loaded_data) `
このように、保存後にjson.loadで読み戻すだけでも、JSONとして壊れていないかを確認できます。さらに、先ほどのcheck_basic_structureのような関数を通すことで、基本構造の確認も同時に行えます。ファイルを作る処理と確認する処理を一連の流れにしておくと、作成ミスに気づきやすくなります。
読み戻し確認では、件数も確認します。元データの件数とfeaturesの件数が一致しているかを見ることで、途中で除外されたデータや、変換エラーで抜けたデータを把握できます。意図的に除外したデータがある場合は、除外件数と理由を別途記録しておくと、後で説明しやすくなります。
`python expected_count = len(records) actual_count = len(loaded_data["features"])
if expected_count != actual_count: raise ValueError("feature count does not match source record count") `
ただし、元データの中に座標が空欄のものや、出力対象外の分類が含まれる場合は、単純な件数一致ではなく、出力対象件数とFeature件数を比較します。重要なのは、なぜその件数になったのかを説明できることです。実務では、GeoJSONの作成結果そのものだけでなく、作成時に除外したレコード、変換できなかったレコード、確認が必要なレコードを残しておくと、品質管理がしやすくなります。
保存後の確認では、ファイルを開いて目視することも有効です。整形されたGeoJSONであれば、type、features、geometry、propertiesの並びを確認できます。すべてを目で確認する必要はありませんが、先頭数件、末尾数件、代表的な分類、座標値の桁数、日本語の表示、Polygonの閉じ方などを見ること で、機械的な検査だけでは気づきにくい違和感を発見できます。
実務でGeoJSONを扱うときの注意点
実務でGeoJSONを使うときは、コードの正しさだけでなく、業務上の前提をそろえることが重要です。まず確認したいのは、座標の基準です。GeoJSONでは経度緯度の座標を扱うのが一般的ですが、元データが平面直角座標やローカル座標の場合、そのまま経度緯度として入れてはいけません。座標系が異なるデータを混ぜると、形は作れても位置は正しくなりません。Pythonで出力する前に、元データがどの座標系で、出力先がどの座標系を期待しているかを確認します。
次に、桁数の扱いです。座標の小数点以下を丸めすぎると、位置精度が落ちます。一方で、元データ以上に細かい桁を持たせても、実際の精度が上がるわけではありません。表示用、共有用、解析用など、GeoJSONの用途によって必要な桁数は変わります。現場記録や点検位置を扱う場合は、取得方法と必要精度を踏まえて、丸め方を決めておくとよいです。
ファイルサイズにも注意が必要です。GeoJSONは人が読みやすい形式ですが、座標点数が非常に多い線や面をそのまま入れると、ファイルが大きくなります。境界線が細かすぎる場合や、同じような点が過剰に含まれている場合は、用途に応じて簡略化や分割を検討することがあります。ただし、形状の簡略化は位置や面積に影響する可能性があるため、安易に行わず、目的と許容範囲を明確にしてから判断します。
属性情報の運用も重要です。GeoJSONは1つのFeatureごとにpropertiesを持てるため、現場で扱う情報を地物に紐づけやすい形式です。しかし、属性名のルールが決まっていないと、後から集計や検索が難しくなります。作成前に、必須項目、任意項目、空欄時の扱い、日付形式、分類名の表記を決めておくと、データの再利用性が高まります。
さらに、GeoJSONを受け渡す相手がいる場合は、相手側が期待する構造を確認しておく必要があります。FeatureCollectionでよいのか、単一Featureが必要なのか、propertiesの項目名は決まっているのか、高さを含めるのか、Polygonの穴を扱えるのかといった点は、使う側の処理によって変わります。GeoJSONとして正しいことと、相手の業務でそのまま使えることは別です。受け渡し前に小さなサンプルを共有し、読み込み結果を確認してから本番データを作ると手戻りを減らせます。
Pythonで自動生成する場合は、エラーの扱いも設計しておくと実務向きになります。座標が空欄の行、数値変換できない行、必須属性が欠けている行、Polygonの点数が足りない行などを、処理の途中で無視してしまうと、後で原因が分からなくなります。出力できなかったデータは、管理番号や理由と一緒に記録しておくと、元データの修正や関係者への確認がしやすくなります。
まとめ
GeoJSONをPythonで作成する基本は、辞書とリストでFeatureCollection、Feature、geometry、propertiesを正しく組み立て、json形式として保存することです。Point、LineString、Polygonはそれぞれcoordinatesの構造が異なるため、座標配列の深さを理解しておく必要があります。特にPolygonでは、外周の最初と最後の座標を一致させ、面が閉じた形になっているかを確認することが重要です。
確認手順としては、まずtypeと階層構造を確認し、次に座標順序と数値型を確認します。そのうえで、Polygonの閉じ方、propertiesの項目名と値、保存後の文字コードと読み戻しを確認します。この5つを作成処理に組み込んでおけば、構文として読めるだけでなく、実務で使いやすいGeoJSONに近づけられます。
GeoJSONは、現地記録、点検情報、施工範囲、設備位置などを位置と結びつけて扱うための便利な形式です。一方で、座標系、桁数、属性定義、受け渡し条件を曖昧にしたまま作成すると、後工程で位置ずれや属性不一致が発生します。Pythonで自動化するほど、最初のルール設計と確認手順が大切になります。
現場で取得した位置情報をGeoJSONとして整理し、写真や点検記録と結びつけて管理したい場合は、取得から確認、共有までの流れを一体で考えることが重要です。スマートフォンなどで取得した位置情報や現地記録を業務に取り入れる場合も、GeoJSON化を見据えて、座標順序、座標系、属性項目、確認手順をあらかじめ整理しておくと、後工程で扱いやすいデータにしやすくなります。
LRTKで現場の測量精度・作業効率を飛躍的に向上
LRTKシリーズは、建設・土木・測量分野における高精度なGNSS測位を実現し、作業時間短縮や生産性の大幅な向上を可能にします。国土交通省が推進するi-Constructionにも対応しており、建設業界のデジタル化促進に最適なソリューションです。
LRTKの詳細については、下記のリンクよりご覧ください。
製品に関するご質問やお見積り、導入検討に関するご相談は、
こちらのお問い合わせフォームよりお気軽にご連絡ください。ぜひLRTKで、貴社の現場を次のステージへと進化させましょう。

