Как стать автором
Обновить

Поиск удобных мест для жизни в Москве на GitHub Pages с помощью DuckDB в браузере

Уровень сложностиПростой
Время на прочтение5 мин
Количество просмотров4.5K

В прошлых статьях и на форумах я получил несколько дельных советов связанных с визуализацией результатов. От агрегированных данных теперь перейдем к детальной инфомации для каждого жилого здания. Продолжим анализировать географию столицы.

Как подготовить для этого данные я детально описывал в "Где 15 минут пешком от дома до метро в Москве" "Где в Москве жить «неплохо»". А в публикации "Жилье в 500м от сетевых продуктовых магазинов в Москве." я столкнулся со специфичным трафиком GitHub Gist с желтушных публикаций. Сообщество OSMеров предложило мне отличный вариант, когда визуализация не требует чтения исходной статьи.

С моей точки зрения у этого варианта был один главный недостаток какой тайл сервер использовать чтобы это было законно. Яндекс карты не раздают свои тайлы для бесплатной аналитики в MapLibre. Завязываться на SDK от Яндекс я не хочу. Это фрагментация - когда в картах по миру используешь один виджет, а для родины другой.

Карты акварелью от Stamen watercolor смотрятся отлично, не отвлекая буквами и резкими линиями. Но у этого провайдера главная загвоздка - работает на localhost, а как размещаешь на сайте - то надо платить за карту. Не мой вариант для хобби, поэтому я продолжил поиски и нашел вектрные CARTO - positron-gl-style, которые отлично подходят для отображения данных.

Также по совету хабровчанина ограничил область отображения только теми регионом, что анализирую.

Метро и магазинов шаговой доступности недостаточно

У меня завершился расчет пешеходных расстояний в Москве для 82768 объектов представляющих интерес (point of interest) от 47324 жилых зданий, расположенных в радиусе 2 км от входов в метро и МЦК. Расчеты делал без Apache Spark, пока не замахнулся на более масштабный анализ по всему миру. Расчеты учитывают подъезды и ближайшие входы (например в парк), если же информации для здания и POI недостаточно, то используется центры объектов на карте. В итоге выгрузил 35 984 392 пешеходных дистанций в parquet файлы общим объемом 198Мб. И все эти маршруты доступны вам для запросов.

Парки, кинотеатры, магазины, школы, театры, обзорные площадки итп. Это достаточно большой объем данных для отображения на карте, тем более для статического сайта без доступа к базе данных. Если за хостинг базы на проприетарных данных нужно платить деньги, то в случае с OSM можно перенести данные в статические выгрузки на github pages, а базу внедрить в сам браузер.

Встречайте DuckDB Wasm

Это встраиваемая колоночная база данных, которая умеет многое из того что делает ClickHouse и PostgreSQL. Этот же проект сделал кросс компиляцию базы данных на WebAssembly в JavaScript. Сайт проекта поможет вам разобраться что же это такое.

Для меня самое главное что эта база данных полнофункциональная, поддерживает чтение сжатых parquet файлов и распространяется по MIT лицензии. Это позволило мне начать разрабатывать логику фильтрации даннных карты прямо в браузере на JavaScript. И не платить за хостинг! Цена этого в том, что производительность произвольных запросов с карт зависит от времени доступа к данным по сети. Хоть база и использует статистику с паркетов и запросы с ranges чтобы не скачивать лишние данные. Вот это настоящий Serverless, а не разные лямбды в облаке.

DuckDB Wasm - новая технология, по сравнению с SQLite это более приспособленная для аналитических запросов база данных, при этом обладающая почти всеми плюсами встриваемой базы данных.

Например так я извлекаю данные о негативных факторов для отображения как GeoJSON на карте:

const stmt = await conn.prepare(
`select distinct type||id id from 'https://igor-suhorukov.github.io/data/direct_distance.parquet' where (category='_air_quality' and distance<=?) or (category='_noisy_place' and distance<=?) or (category='_industrial' and distance<=?) or (category='_dangerous' and distance<=?) or (category='_mosquitoes' and distance<=?) or (category='_sad_place' and distance<=?)`
);

    const ecologyMetrics = await stmt.query(
        document.querySelector('#_air_quality').value,
        document.querySelector('#_noisy_place').value,
        document.querySelector('#_industrial').value,
        document.querySelector('#_dangerous').value,
        document.querySelector('#_mosquitoes').value,
        document.querySelector('#_sad_place').value
      );

    var newGeo ={"type": "FeatureCollection", "features": []};
    for(const id of ecologyMetrics.toArray().map((row) => row.toJSON().id)){
     const building= buildings[id];
     if(building){
         building.properties.fill='red';
         newGeo.features.push(building);
     }
    }

Как будем отображать информацию о детсадах, школах и поликлиниках?

Все POI разделены на категории. В выгрузках содержатся пути к школам, коледжам/техникумам и институтам, разным курсам и тренингам. Так же как и все что связано с детьми - детские сады, игровые площадки, панда парки, зоопарки есть в детальной информации.

Может кто нибудь из вас сталкивался с Open Source проектами на JS/HTML, отображающими карточки с детальной информцией из OSM для POI? В сообществе порекомендовали openpoimap.org но лицензия на его код не понятна, как и не все что нужно мне отображать там есть.

Поскольку целевая аудитория моих публикаций сейчас - это программисты и OSMры, то начнем с простого интерфейса, в виде запросов в поле "Что ищем:" amenity='school' and distance < 1500 выводит жилые дома ближе 1.5км пешком от территории школ. Жилые дома amenity='kindergarten' and distance < 500 доступные пешком в 500м от детсадов. Для поиска жилья в радиусе 500м от сетевых продуктовых магазинов: distance <= 500 and (shop='supermarket' or shop='convenience') and brand is not null Когда жилой дом расположен на расстоянии от 300 до 1500м от входа в метро (network='Московский метрополитен' or network='МЦК') and 'transport'=any(categories) and distance between 300 and 1500

Схема данных сейчас такая:

select * from parquet_scan('building_distance/data*.parquet') limit 0;
┌───────┬───────┬─────────────┬───────────────┬──────────┬────────────┬─────────┬─────────┬─────────┬───┬──────────┬───────────┬──────────┬───────────┬──────────┬─────────┬─────────┬─────────┬──────────┐
│ part  │ h3_8  │ building_id │ building_type │ distance │ categories │  name   │ amenity │ leisure │ … │ historic │   sport   │ memorial │ education │ religion │ office  │  brand  │ network │ operator │
│ int32 │ int32 │    int64    │    varchar    │  int16   │ varchar[]  │ varchar │ varchar │ varchar │   │ varchar  │ varchar[] │ varchar  │  varchar  │ varchar  │ varchar │ varchar │ varchar │ varchar  │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 0 rows                                                                                                  │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Run Time (s): real 0.002 user 0.002213 sys 0.000000

D .mode line
D select * from parquet_scan('building_distance/data*.parquet') limit 1;
         part = 0
         h3_8 = 296373465
  building_id = 24241948
building_type = ways
     distance = 110
   categories = [education]
         name = Школа № 1507. Корпус школьного образования
      amenity = school
      leisure = 
         shop = 
   healthcare = 
      tourism = 
     historic = 
        sport = 
     memorial = 
    education = 
     religion = 
       office = 
        brand = 
      network = 
     operator = 
Run Time (s): real 0.020 user 0.062543 sys 0.027150
Дома в 500м до сетевых продуктовых магазинов обозначены оранжевым
Дома в 500м до сетевых продуктовых магазинов обозначены оранжевым

Как сформирую список типовых запросов, то сделаю выбор интересующей информации в интерфейсе более "человечно". Текущих данных уже хватает, чтобы сформировать окно с информацией об окрестностях для каждого жилого дома на карте.

Итог

Благодаря OpenStreetMap + PostgreSQL живем во время, когда можно самостоятельно рассчитать и перепроверить геоданные на ноутбуке. Раньше это требовало бигдаты и бюджетов на аналитику и доступа к API расчета маршрутов за деньги... Теперь хватает open data + open source. Уверен, что через пару лет ChatGPT сможет выдать ответ на любой вопрос пользователя по геоаналитике, сформировав и отправив SQL запрос к гео данным. Встраиваемая база данных DuckDB очень быстро развивается, сообщество оптимизирует ее производительность и теперь доступна и в браузере через WebAssembly и позволяет сайту быть Serverless и работать с данными на клиенте.

Интерфейс "Москва, где мне комфортно" позволяет регулировать дистанцию и отображать данные как по негативным факторам, так и по позитивным. Карта доступна по адресу https://igor-suhorukov.github.io и является моим субъективным методом поиска жилья в мегаполисе на основе открытых геоданных. Работает карта на смартфоне и в браузере на компьютере.

Теги:
Хабы:
Всего голосов 19: ↑19 и ↓0+19
Комментарии4

Публикации

Истории

Работа

Ближайшие события

One day offer от ВСК
Дата16 – 17 мая
Время09:00 – 18:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург
Summer Merge
Дата28 – 30 июня
Время11:00
Место
Ульяновская область