Microsoft SQLは、mssql-pythonドライバーを通じて使用できる2つの空間データ型を提供しています。 座標が何を表すかに基づいてタイプを選びます:
| タイプ | 説明 | 利用シーン |
|---|---|---|
geography |
地球円座標系 | GPS座標、地図、地球表面のあらゆるもの。 距離はメートルで表されます。 SRID 4326(WGS 84)はGPSの標準規格です。 |
geometry |
平面座標系 | 間取り図、CAD図面、ゲームワールド、または任意のデカルト座標系。 距離は座標系の単位で表されます。 |
空間データを挿入
geography::Point() などの Microsoft SQL のコンストラクター関数を使用するか、Well-Known Text (WKT) 文字列を渡してください。
地理データ(ポイント)
緯度、経度、SRIDを持つポイント構成ツールを使って地理的なポイントを挿入します。
import mssql_python
connection_string = "Server=<server>.database.windows.net;Database=AdventureWorks2022;Authentication=ActiveDirectoryDefault;Encrypt=yes"
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
# Insert a geographic point (longitude, latitude)
# Note: Microsoft SQL uses (longitude, latitude) order
cursor.execute("CREATE TABLE #Locations (Name NVARCHAR(100), GeoLocation GEOGRAPHY)")
cursor.execute("""
INSERT INTO #Locations (Name, GeoLocation)
VALUES (%(name)s, geography::Point(%(lat)s, %(lon)s, 4326))
""", {"name": "Seattle", "lat": 47.6062, "lon": -122.3321})
conn.commit()
WKT(Well-Known Text)から地理データを作成
ポイント、ラインストリング、ポリゴンをサポートするWell-Known Text(WKT)形式を使用して地理データを挿入します。
# Well-Known Text format
wkt_point = "POINT(-122.3321 47.6062)"
wkt_line = "LINESTRING(-122.3321 47.6062, -122.4194 37.7749)"
wkt_polygon = "POLYGON((-122.40 47.60, -122.30 47.60, -122.30 47.65, -122.40 47.65, -122.40 47.60))"
cursor.execute("DROP TABLE IF EXISTS #Locations")
cursor.execute("CREATE TABLE #Locations (Name NVARCHAR(100), GeoLocation GEOGRAPHY)")
cursor.execute("""
INSERT INTO #Locations (Name, GeoLocation)
VALUES (%(name)s, geography::STGeomFromText(%(wkt)s, 4326))
""", {"name": "Route", "wkt": wkt_line})
conn.commit()
幾何学データ
CAD図面や間取り図用に点やポリゴンを使って平面ジオメトリデータを挿入します。
# Insert a geometry point (flat coordinate system)
cursor.execute("CREATE TABLE #FloorPlan (RoomName NVARCHAR(100), RoomShape GEOMETRY)")
cursor.execute("""
INSERT INTO #FloorPlan (RoomName, RoomShape)
VALUES (%(name)s, geometry::Point(%(x)s, %(y)s, 0))
""", {"name": "Office 101", "x": 50.0, "y": 100.0})
# Insert a geometry polygon
room_wkt = "POLYGON((0 0, 0 10, 20 10, 20 0, 0 0))"
cursor.execute("""
INSERT INTO #FloorPlan (RoomName, RoomShape)
VALUES (%(name)s, geometry::STGeomFromText(%(wkt)s, 0))
""", {"name": "Conference Room", "wkt": room_wkt})
conn.commit()
空間データのクエリ
STAsText()やLat/Longプロパティのような空間的手法を使って座標を読みやすい形式で取得します。
テキストとして取得
空間座標を Well-Known Text 形式で、緯度および経度のプロパティとともに取得します。
cursor.execute("""
SELECT
City,
SpatialLocation.STAsText() AS LocationWKT,
SpatialLocation.Lat AS Latitude,
SpatialLocation.Long AS Longitude
FROM Person.Address
WHERE SpatialLocation IS NOT NULL
""")
for row in cursor.fetchall()[:5]:
print(f"{row.City}: {row.Latitude}, {row.Longitude}")
print(f" WKT: {row.LocationWKT}")
GeoJSON として取得
Microsoft SQLはGeoJSON変換をサポートしています。 SQL Server 2017+では、文字列関数を使って直接GeoJSONを構築でき、以下のようにPythonで変換できます:
cursor.execute("""
SELECT
City,
SpatialLocation.STAsText() AS WKT
FROM Person.Address
WHERE SpatialLocation IS NOT NULL AND City = %(city)s
""", {"city": "Seattle"})
for row in cursor:
print(f"{row.City}: {row.WKT}")
近くのポイントを探す
空間距離関数を使って、基準点から一定距離内のすべての場所を見つけます。
# Find locations within 10 km of Seattle
cursor.execute("""
DECLARE @seattle geography = geography::Point(47.6062, -122.3321, 4326);
SELECT
City,
SpatialLocation.STDistance(@seattle) / 1000 AS DistanceKM
FROM Person.Address
WHERE SpatialLocation IS NOT NULL
AND SpatialLocation.STDistance(@seattle) < 50000 -- 50 km in meters
ORDER BY SpatialLocation.STDistance(@seattle)
""")
for row in cursor.fetchall()[:5]:
print(f"{row.City}: {row.DistanceKM:.2f} km away")
多角形内の点を見つける
地理的多角形領域と交差する、またはその中に位置するすべての点を見つけます。
# Find all addresses within a region
cursor.execute("""
DECLARE @region geography = geography::STPolyFromText(
'POLYGON((-122.5 47.5, -122.2 47.5, -122.2 47.7, -122.5 47.7, -122.5 47.5))',
4326
);
SELECT City, SpatialLocation.STAsText() AS Location
FROM Person.Address
WHERE SpatialLocation IS NOT NULL
AND @region.STIntersects(SpatialLocation) = 1
""")
距離を計算する
Microsoft SQLのSTDistance()メソッドは、地理タイプに対してメートル単位の距離を返します。
2点間の距離
2つの地理的ポイント間の距離をキロメートルで計算するヘルパー機能を作成します。
def get_distance_km(cursor, point1: tuple, point2: tuple) -> float:
"""Calculate distance between two points in kilometers."""
cursor.execute("""
DECLARE @point1 geography = geography::Point(%(lat1)s, %(lon1)s, 4326);
DECLARE @point2 geography = geography::Point(%(lat2)s, %(lon2)s, 4326);
SELECT @point1.STDistance(@point2) / 1000 AS DistanceKM;
""", {
"lat1": point1[0], "lon1": point1[1],
"lat2": point2[0], "lon2": point2[1]
})
return cursor.fetchval()
# Seattle to San Francisco
distance = get_distance_km(cursor, (47.6062, -122.3321), (37.7749, -122.4194))
print(f"Distance: {distance:.2f} km")
面積を計算してください
地理的地域の面積を平方キロメートルで計算します。
cursor.execute("""
DECLARE @region geography = geography::STPolyFromText(
'POLYGON((-122.5 47.5, -122.2 47.5, -122.2 47.7, -122.5 47.7, -122.5 47.5))',
4326
);
SELECT
'Seattle Region' AS Name,
@region.STArea() / 1000000 AS AreaSqKm
""")
row = cursor.fetchone()
print(f"{row.Name}: {row.AreaSqKm:.2f} sq km")
空間操作
Microsoft SQLは、ユニオン、交差、バッファなどの空間オブジェクトに対するセット操作をサポートしています。
図形の和集合
2つの地理的ポリゴンを1つの形状に組み合わせ、総面積を計算します。
cursor.execute("""
DECLARE @parcel1 geography = geography::STPolyFromText(
'POLYGON((-122.35 47.60, -122.33 47.60, -122.33 47.62, -122.35 47.62, -122.35 47.60))',
4326
);
DECLARE @parcel2 geography = geography::STPolyFromText(
'POLYGON((-122.34 47.61, -122.32 47.61, -122.32 47.63, -122.34 47.63, -122.34 47.61))',
4326
);
DECLARE @combined geography = @parcel1.STUnion(@parcel2);
SELECT @combined.STAsText() AS CombinedWKT,
@combined.STArea() / 1000000 AS TotalAreaKM
""")
row = cursor.fetchone()
print(f"Combined area: {row.TotalAreaKM:.2f} sq km")
交差点
2つの地理的地域が交差する重なる領域を見つけてください。
polygon1_wkt = "POLYGON((-122.35 47.60, -122.33 47.60, -122.33 47.62, -122.35 47.62, -122.35 47.60))"
polygon2_wkt = "POLYGON((-122.34 47.61, -122.32 47.61, -122.32 47.63, -122.34 47.63, -122.34 47.61))"
cursor.execute("""
DECLARE @region1 geography = geography::STPolyFromText(%(wkt1)s, 4326);
DECLARE @region2 geography = geography::STPolyFromText(%(wkt2)s, 4326);
SELECT
@region1.STIntersection(@region2).STAsText() AS IntersectionWKT,
@region1.STIntersection(@region2).STArea() / 1000000 AS AreaKM
""", {"wkt1": polygon1_wkt, "wkt2": polygon2_wkt})
バッファ(領域を拡張)
地理的なポイントの周囲にバッファゾーンを作成し、そのバッファ半径内のすべての場所を見つけます。
# Find all addresses within 5km buffer of a point
cursor.execute("""
DECLARE @center geography = geography::Point(47.6062, -122.3321, 4326);
DECLARE @buffer geography = @center.STBuffer(5000); -- 5 km buffer
SELECT City
FROM Person.Address
WHERE SpatialLocation IS NOT NULL
AND @buffer.STIntersects(SpatialLocation) = 1
""")
Python統合
Microsoft SQLからWKT文字列を取得し、Shapelyライブラリで解析してクライアントサイドのジオメトリ処理を行います。
Shapelyライブラリを使用して
インストールは pip install shapelyを実行することです。
from shapely import wkt
from shapely.geometry import Point, Polygon
# Retrieve spatial data from Person.Address
cursor.execute("""
SELECT City, SpatialLocation.STAsText() AS WKT
FROM Person.Address
WHERE SpatialLocation IS NOT NULL AND City IN ('Seattle', 'Redmond')
""")
for row in cursor:
# Parse WKT into Shapely geometry
geom = wkt.loads(row.WKT)
if isinstance(geom, Point):
print(f"{row.City}: Point at ({geom.x}, {geom.y})")
elif isinstance(geom, Polygon):
print(f"{row.City}: Polygon with area {geom.area}")
Shapelyで空間データを作成
Shapelyライブラリを使って幾何学的オブジェクトを作成し、それらをWKT形式に変換してMicrosoft SQLに挿入します。
from shapely.geometry import Point, Polygon, LineString
from shapely import wkt
# Create geometries in Python
seattle = Point(-122.3321, 47.6062)
seattle_wkt = wkt.dumps(seattle)
route = LineString([(-122.3321, 47.6062), (-122.4194, 37.7749)])
route_wkt = wkt.dumps(route)
# Insert into a temp table
cursor.execute("CREATE TABLE #Routes (Name NVARCHAR(100), Path NVARCHAR(MAX))")
cursor.execute("""
INSERT INTO #Routes (Name, Path)
VALUES (%(name)s, %(wkt)s)
""", {"name": "Seattle to SF", "wkt": route_wkt})
cursor.execute("SELECT Name, Path FROM #Routes")
row = cursor.fetchone()
print(f"{row.Name}: {row.Path[:40]}...")
GeoJSONに変換
WKT形式の空間データをGeoJSONに変換し、ウェブベースのアプリケーションや地図サービス向けに使えます。
import json
from shapely import wkt
from shapely.geometry import mapping
cursor.execute("""
SELECT City, SpatialLocation.STAsText() AS WKT
FROM Person.Address
WHERE SpatialLocation IS NOT NULL AND City = 'Seattle'
""")
row = cursor.fetchone()
geom = wkt.loads(row.WKT)
geojson = {
"type": "Feature",
"properties": {"name": row.City},
"geometry": mapping(geom)
}
print(json.dumps(geojson, indent=2))
GeoDataFrameの統合
Microsoft SQLの空間データをGeoPandas GeoDataFrameに読み込み、高度な地理空間解析を行います。
import geopandas as gpd
from shapely import wkt
import pandas as pd
cursor.execute("""
SELECT AddressID, City, SpatialLocation.STAsText() AS WKT
FROM Person.Address
WHERE SpatialLocation IS NOT NULL AND City = 'Seattle'
""")
# Build DataFrame
rows = cursor.fetchall()
df = pd.DataFrame(
[(r.AddressID, r.City, r.WKT) for r in rows],
columns=['id', 'city', 'wkt']
)
# Convert to GeoDataFrame
df['geometry'] = df['wkt'].apply(wkt.loads)
gdf = gpd.GeoDataFrame(df, geometry='geometry', crs="EPSG:4326")
# Now use GeoPandas operations
print(gdf.head())
空間インデックス
Microsoft SQLで空間インデックスを作成し、大規模な空間データセットへのクエリを加速します。
-- Geography index on Person.Address
CREATE SPATIAL INDEX SIX_Address_SpatialLocation
ON Person.Address(SpatialLocation)
USING GEOGRAPHY_GRID
WITH (
GRIDS = (LEVEL_1 = MEDIUM, LEVEL_2 = MEDIUM, LEVEL_3 = MEDIUM, LEVEL_4 = MEDIUM),
CELLS_PER_OBJECT = 16
);
-- Geometry index (example with custom table)
CREATE SPATIAL INDEX SIX_FloorPlan_RoomShape
ON dbo.FloorPlan(RoomShape)
USING GEOMETRY_GRID
WITH (
BOUNDING_BOX = (0, 0, 1000, 1000),
GRIDS = (LEVEL_1 = HIGH, LEVEL_2 = HIGH, LEVEL_3 = HIGH, LEVEL_4 = HIGH)
);
パフォーマンスに関するヒント
これらの手法を適用して、空間クエリを大規模に効率的に行いましょう。
空間インデックスヒントを活用してください
空間インデックスを使って大規模な空間データセットのクエリを加速させましょう。
cursor.execute("""
DECLARE @region geography = geography::STPolyFromText(
'POLYGON((-122.5 47.5, -122.2 47.5, -122.2 47.7, -122.5 47.7, -122.5 47.5))',
4326
);
SELECT City
FROM Person.Address
WHERE SpatialLocation IS NOT NULL
AND SpatialLocation.STIntersects(@region) = 1
""")
まずフィルターをかけてから計算します
空間クエリを最適化するには、まず高速バウンディングボックスフィルターを適用し、その後正確な距離計算を行います。
# Approximate filter with bounding box, then precise calculation
cursor.execute("""
DECLARE @center geography = geography::Point(47.6062, -122.3321, 4326);
SELECT City, SpatialLocation.STDistance(@center) AS Distance
FROM Person.Address
WHERE SpatialLocation IS NOT NULL
AND SpatialLocation.Filter(@center.STBuffer(10000)) = 1 -- Fast bounding box filter
AND SpatialLocation.STDistance(@center) < 10000 -- Precise distance check
ORDER BY Distance
""")
表示の精度を下げる
座標の精度を下げることで空間ジオメトリを簡略化し、表示を進めましょう。
cursor.execute("""
SELECT
City,
SpatialLocation.Reduce(100).STAsText() AS SimplifiedWKT -- 100 meter tolerance
FROM Person.Address
WHERE SpatialLocation IS NOT NULL AND City = 'Seattle'
""")
座標参照システム
SRIDは座標系を決定し、Microsoft SQLの距離や面積の計算方法に影響を与えます。
一般的なSRID値
SRID(空間参照識別子)は、あなたのデータの座標系を定義します。 間違ったSRIDを使用すると、誤った距離や面積の計算が起こります。
| SRID | 名前 | 利用シーン |
|---|---|---|
| 4326 | WGS 84 | GPS座標、ウェブマッピング |
| 4269 | NAD 83 | 北米の調査 |
| 0 | SRIDはなし | 平面幾何学、局所座標 |
SRID間の変換
空間データを取得し、座標系を検証するためにSRIDを確認しましょう。
cursor.execute("""
-- Geography is always round-earth, but SRID defines datum
SELECT SpatialLocation.STAsText() AS WKT,
SpatialLocation.STSrid AS SRID
FROM Person.Address
WHERE SpatialLocation IS NOT NULL
""")
ベスト プラクティス
これらのガイドラインを適用して空間データを正しく効率的に扱いましょう。
ジオメトリの検証
処理前に空間データの妥当性を確認し、幾何学的問題を特定してください。
cursor.execute("""
SELECT
City,
SpatialLocation.STIsValid() AS IsValid,
SpatialLocation.IsValidDetailed() AS InvalidReason
FROM Person.Address
WHERE SpatialLocation IS NOT NULL
AND SpatialLocation.STIsValid() = 0
""")
for row in cursor:
print(f"Invalid: {row.City} - {row.InvalidReason}")
ジオメトリを有効にする
MakeValid()メソッドを使って無効な幾何学的形状を自動的に修正します。
# Example with a temp table
cursor.execute("""
CREATE TABLE #SpatialFix (Name NVARCHAR(100), GeoLocation GEOGRAPHY);
INSERT INTO #SpatialFix VALUES ('Test', geography::STGeomFromText('POLYGON((0 0, 0 1, 1 0, 0 0))', 4326));
UPDATE #SpatialFix
SET GeoLocation = GeoLocation.MakeValid()
WHERE GeoLocation.STIsValid() = 0
""")
適切なタイプを選ぶ
geographyとgeometryの選択によって、Microsoft SQLが距離や面積をどのように計算するかが決まります。
- 地理:実際の位置(GPSポイント)、地表計算、メートル単位の距離。
- 幾何学:平面図(平面図、CAD)、デカルト座標系、またはSRIDが重要でない場合。