Координаты в языке Python: от простых до геоинформационных

Раздел: Основы Python -> Работа с координатами и геоданными

Основные подходы к работе с координатами в Python

Как вычислить расстояние между двумя географическими координатами с высокой точностью?

Наиболее эффективным речением для работы с геоданными является использование библиотеки geopy. Она предоставляет различные модели эллипсоида (WGS-84) и методы вычисления расстояний (Vincenty, geodesic).

from geopy.distance import geodesic

point_a = (55.7558, 37.6173)  # Москва
point_b = (59.9343, 30.3351)  # Санкт-Петербург
distance = geodesic(point_a, point_b).kilometers
print(f"Расстояние: {distance:.2f} км")

координаты python (координаты в python)

Расстояние: 633.70 км

Здесь координаты задаются кортежами (широта, долгота). Функция geodesic возвращает объект с методами для получения расстояния в разных единицах (км, мили, метры).

Типичная ошибка: перепутать порядок аргументов (долгота, широта). В geopy всегда первая широта. Если передать наоборот, расстояние будет неверным. Также при использовании больших наборов данных следует учитывать производительность: для массовых вычислений лучше использовать векторизованные решения.

Как вычислить расстояние на плоскости без учёта кривизны Земли?

Если координаты представлены в декартовой системе (например, проекция UTM) или работа ведётся с локальными координатами, подойдёт стандартная библиотека math.

import math

x1, y1 = 0.0, 0.0
x2, y2 = 3.0, 4.0
distance = math.hypot(x2 - x1, y2 - y1)
print(f"Евклидово расстояние: {distance}")
Евклидово расстояние: 5.0

Функция hypot вычисляет квадратный корень суммы квадратов, избегая переполнения. Для большого количества точек используйте списковые включения или NumPy.

Ограничение: math.hypot не подходит для географических широты/долготы, так как градусы долготы имеют разную длину в зависимости от широты. Результат будет искажён.

Как работать с трёхмерными координатами и преобразованиями?

Для трёхмерных векторов (x, y, z) удобно использовать библиотеку numpy. Она позволяет выполнять операции над целыми массивами координат.

import numpy as np

points = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]])
origin = np.array([0.0, 0.0, 0.0])
distances = np.linalg.norm(points - origin, axis=1)
print(distances)
[3.74165739 8.77496439 13.92838828]

Здесь каждая строка в массиве points – координаты точки. Вычитание origin транслируется, а np.linalg.norm с axis=1 вычисляет длину каждого вектора.

Проблема: при работе с географическими координатами (широта, долгота, высота) неверно применять евклидово расстояние, если высота задана в метрах, а широта/долгота в градусах. Требуется сначала перевести в одну систему (например, геоцентрическую).

Как пересчитать широту и долготу в декартовы координаты (проекция Гаусса-Крюгера)?

Для пересчёта географических координат в метрическую проекцию используется библиотека pyproj. Она основана на PROJ и поддерживает тысячи систем координат.

import pyproj

transformer = pyproj.Transformer.from_crs("EPSG:4326", "EPSG:32637", always_xy=True)
lon, lat = 37.6173, 55.7558
x, y = transformer.transform(lat, lon)  # если always_xy=False, то (lat, lon)
print(f"X: {x:.2f}, Y: {y:.2f}")
X: 399166.32, Y: 6182731.83

Обратите внимание на параметр always_xy: если True, то входные аргументы в порядке (долгота, широта), что соответствует привычному порядку в GIS. Иначе – наоборот.

Типичная ошибка: игнорирование зоны UTM. Код EPSG:32637 соответствует зоне 37N (Москва). Для других регионов зона может отличаться. Использование неправильной проекции даёт большие искажения.

Дополнительные примеры работы с координатами

Создание класса для хранения и работы с точками

Пример
class Point:
    def __init__(self, x, y, z=0.0):
        self.x = x
        self.y = y
        self.z = z
    
    def distance_to(self, other):
        import math
        dx = self.x - other.x
        dy = self.y - other.y
        dz = self.z - other.z
        return math.sqrt(dx*dx + dy*dy + dz*dz)
    
    def __repr__(self):
        return f"Point({self.x}, {self.y}, {self.z})"

p1 = Point(1, 2, 3)
p2 = Point(4, 6, 8)
print(p1.distance_to(p2))
7.0710678118654755

Класс инкапсулирует координаты и предоставляет метод distance_to. Можно легко расширить для работы с геодезическими расстояниями, добавив метод geodesic_distance с использованием geopy.

Преобразование между системой координат в пакетном режиме

Пример
import pyproj
import numpy as np

transformer = pyproj.Transformer.from_crs("EPSG:4326", "EPSG:3857", always_xy=True)
# Массив координат (долгота, широта)
lons = np.array([30.0, 37.0, 55.0])
lats = np.array([60.0, 55.0, 25.0])
x, y = transformer.transform(lats, lons)  # без always_xy: (lat, lon)
print("X:", x)
print("Y:", y)
X: [3339584.72 4118384.42 6122474.06]
Y: [8391132.63 7367166.14 2871162.12]

Проекция EPSG:3857 (Web Mercator) широко используется в веб-картах. Преобразование выполняется для всех точек одновременно за счёт numpy.

Вычисление азимута и расстояния между двумя географическими точками

Пример
from geopy.distance import distance
from geopy.point import Point
from math import atan2, degrees

def bearing(pt_a, pt_b):
    lat1, lon1 = map(radians, pt_a)
    lat2, lon2 = map(radians, pt_b)
    dlon = lon2 - lon1
    x = sin(dlon) * cos(lat2)
    y = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlon)
    initial_bearing = atan2(x, y)
    return (degrees(initial_bearing) + 360) % 360

from math import radians, sin, cos

moscow = (55.7558, 37.6173)
spb = (59.9343, 30.3351)
dist = distance(moscow, spb).kilometers
bearing_deg = bearing(moscow, spb)
print(f"Расстояние: {dist:.1f} км, азимут: {bearing_deg:.1f}°")
Расстояние: 633.7 км, азимут: 327.0°

Азимут – это угол от севера по часовой стрелке. Функция bearing использует формулу сферического треугольника. Для больших расстояний на эллипсоиде точность снижается, лучше использовать геодезический метод (geopy.distance.GeodesicDistance).

Работа с многоугольниками и геометрическими объектами через shapely

Пример
from shapely.geometry import Point, Polygon, LineString

point = Point(30.0, 60.0)
polygon = Polygon([(25.0, 55.0), (35.0, 55.0), (35.0, 65.0), (25.0, 65.0)])
print("Точка внутри полигона?", polygon.contains(point))
line = LineString([(20.0, 50.0), (40.0, 70.0)])
intersection = polygon.intersection(line)
print("Пересечение линии с полигоном:", intersection)
Точка внутри полигона? True
Пересечение линии с полигоном: LINESTRING (30 60, 35 65)

Shapely предоставляет высокоуровневые операции с геометрией, включая буферы, объединения, расстояния. Важно: координаты в shapely – декартовы (x, y). Для географических координат используйте проекцию или учитывайте искажения.

Координаты в Python - comments

En
координаты python (python)