Представьте, что вы пишете приложение для спортивного туризма или логистики беспилотников и вам срочно понадобилось узнать точные границы всех 16 тысяч американских гольф-полей. Звучит как задача для дата-сайентиста с бездонным бюджетом на корпоративные API? Как бы не так. В мире геопространственных данных (GIS) часто царит парадокс: отслеживать каждый корабль в океане легко, а получить бесплатную базу лужаек для гольфа — квест с препятствиями (ведь заставить разработчиков добровольно оторваться от монитора и выйти на настоящую лужайку — задача куда сложнее). Меня, как инженера, всегда раздражал этот искусственный барьер вокруг географических полигонов, ведь поле для гольфа — это всего лишь набор координат, который прекрасно поддается автоматическому анализу.
В этой статье я покажу, как написал пайплайн на Python, который автоматически обнаружил, очистил и экспортировал координаты более чем 16 000 гольф-полей на территории США. Никаких платных подписок и регистраций — только чистый open-source код и геоданные.
Архитектура проекта и выбор источников данных
Попытка парсить коммерческие каталоги через веб-скрейпинг отпала сразу: это юридически серая зона, а точность пространственной привязки там минимальная (в лучшем случае — почтовый индекс центра клуба вместо реальных контуров).
Единственным надежным и бесплатным источником векторных геоданных стал OpenStreetMap (OSM). Тегирование гольф-полей в OSM стандартизировано:
leisure=golf_course— основной тег территории всего комплекса.golf=hole,golf=green,golf=fairway,golf=tee— детализация внутренней структуры.
США — огромная страна, поэтому объем сырых данных в формате OSM PBF исчисляется гигабайтами. Для быстрой фильтрации и извлечения объектов я задействовал связку Python и библиотеки osmium.
Реализация пайплайна на Python
Когда гигабайты картографических дампов начинают плавить процессор (заставляя ваш ноутбук взлетать громче, чем тестовый билд в Kubernetes), на помощь приходит проверенный GIS-стек. Давайте превратим этот массив данных в структурированный GeoJSON.
Для обработки геометрии и фильтрации полигонов я использовал стандартный стек для GIS-разработки: geopandas, shapely и pyrosmium. Ниже представлен базовый скрипт для извлечения полигонов гольф-полей из дампа OSM:
import osmium
import geopandas as gpd
from shapely.geometry import Polygon
class GolfCourseHandler(osmium.SimpleHandler):
def __init__(self):
super(GolfCourseHandler, self).__init__()
self.features = []
def area(self, a):
# Проверяем теги на соответствие гольф-полю
if a.tags.get('leisure') == 'golf_course':
nodes = [(n.lon, n.lat) for n in a.nodes]
if len(nodes) > 2:
self.features.append({
'name': a.tags.get('name', 'Unknown'),
'geometry': Polygon(nodes)
})
# Запуск парсера по региональному дампу
handler = GolfCourseHandler()
handler.apply_file("us-midwest-latest.osm.pbf")
# Конвертация в GeoDataFrame
gdf = gpd.GeoDataFrame(handler.features, crs="EPSG:4326")
gdf.to_file("golf_courses.geojson", driver="GeoJSON")
Очистка данных и борьба с артефактами OSM
Запустив скрипт на всю страну, вы быстро поймете главное правило краудсорсинга: люди рисуют карты по-разному. В процессе компиляции базы я столкнулся с тремя главными проблемами:
- Незамкнутые полигоны: пользователи часто ошибаются при рисовании мультиполигонов, из-за чего геометрия «ломается» (прямо как чужой легаси-код пятничным вечером).
- Дубликаты: крупные комплексы могут состоять из десятков мелких пересекающихся тегов.
- Ложноположительные срабатывания: парсер захватывал лужайки возле отелей или парки с одной мини-лункой для гольфа.
Для решения этих проблем я добавил этап постобработки с исп...
Итог: Создание собственного датас