Представьте, что вы пишете приложение для спортивного туризма или логистики беспилотников и вам срочно понадобилось узнать точные границы всех 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

Запустив скрипт на всю страну, вы быстро поймете главное правило краудсорсинга: люди рисуют карты по-разному. В процессе компиляции базы я столкнулся с тремя главными проблемами:

  • Незамкнутые полигоны: пользователи часто ошибаются при рисовании мультиполигонов, из-за чего геометрия «ломается» (прямо как чужой легаси-код пятничным вечером).
  • Дубликаты: крупные комплексы могут состоять из десятков мелких пересекающихся тегов.
  • Ложноположительные срабатывания: парсер захватывал лужайки возле отелей или парки с одной мини-лункой для гольфа.

Для решения этих проблем я добавил этап постобработки с исп...

Итог: Создание собственного датас