Районы… Кварталы…

    Совсем недавно на хабре была статья от AirBnb — «Создавая карту мира». Хорошая и красивая статья про административное деление мира. Один минус — у статьи один комментарий, и то мой.
    Пользуясь случаем проведу опрос — хотели ли бы вы такую карту административных делений?
    А то она у меня есть:



    Вы наверное замечали, что Google.Карты умеют подсвечивать контура городов. С недавнего времени такое есть и на Яндекс.Картах. Мало кто знает, что геометрия есть и на eSosedi.

    А вот когда такая возможность появится на вашем сайте — теперь зависит только от тебя %username%.

    Для достижения эффекта достаточно зайти на data.esosedi.org или GitHub, ознакомиться с документацией библиотеки osmeRegions и начать использовать.

    P.S.: 3 признака того, что год минувший все сделал красиво: 1. Районы 2. Кварталы. 3. Детализация до «Жилые массивы» доступна для некоторых городов.

    OSMeRegions — это и библиотека и сервис, который позволяет отображать данные административного деления на основе OpenStreetMap на карте. Старшый брат модуля регионов Яндекс.Карт.

    Изначальная задача заключалась в попытке улучшить адресацию гео-привязанных обьектов на esosedi. Потому что работа у нас такая. Как говорилось на спарке:
    «Представим, что в вашем городе есть Улица Ленина. Так вот — в соседнем городе она тоже есть».

    Все началось с стандарта ISO3166-2, который определяет буквенную индексацию стран и регионов. Москва, для примера — RU-MOW. Лично мне никогда не нравилось адресация обьектов на «гео» сайтах — в урле нет никакого «ЧПУ подобного» указания локации. Все ссылки одинаковые и не понятно про что идет речь. Одновременно были проблемы с геокодером — с определением административного адреса.
    В общем это была присказка, впереди было обьединение данных OSM с GeoNames, парсинг Wikipedia, реализация прямых и обратный геокодеров, нахождение смерти Кащеевой, и написание пачки «детранслитеризаторов» чтобы получить читабельные нашим человеком названия регионов.


    (не умею я такие гифки делать, картинка из топика AirBnb)

    Что вы хотите — раскрашивать страны по некой схеме, или делать из Новой Москвы Старую — ваше дело.

    Команда только одна:

    osmeRegions.geoJSON(addr, options, callback)
    


    Где addr — имя региона мира(или просто мира), код страны или региона в формате 3166-2, или OpenStreetMap RelationId. А остальное — схемы, фильтры, рекомбинации — по желанию.
    Hint: relationId можно получить зайдя на нужное место на esosedi — вся информация так указана. Либо воспользоваться «навигатором» на data.esosedi.org — он для этого и создан.
    Это не какой-то там topojson, это АПИ к «ручке» которая готова отдавать вам данные по паре сотен тысяч административных элементов. При этом из ручки выходит GeoJSON, которым можно кормить тот же d3. В общем это решение.

    Мир
    Большая Москва
    Старая Москва (рекомбинация)
    Новая Москва
    Крым Наш (рекомбинация)
    Крым Ваш

    Рекомбинация — это когда уже на клиенте вы сами определяете как собрать регионы.

    P.S.: Сервис может содержать, содержит и будет содержать ошибки.
    P.P.S.: Ручка отдачи данных будет работать «вечно» на свой страх и риск. Когда начинала писаться эта статья я хотел дополнить эту фразу «покуда евро по 70 не будет». Как в воду глядел. Но ни что не мешает сохранять json файлы данных себе.
    P.P.P.S.: Лицензия простая, но не одна — esosedi(CC BY-SA), OSM(CC-BY-SA+ODbL), Wikipedia(CC BY-SA), GeoNames(CC BY). Отдельное спасибо всеми забытому geo.webnabor.com.
    Образно говоря:
    esosedi, « Участники OpenStreetMap», Wikipedia, GeoNames(Хотя они и не требуют)


    Эта история началась 7 лет назад, в 2008 году, в глубинах Wikimapia. Это топик писался два раза с промежутком в год, и в итоге написан на полтора года позже первых сроков. Было много интерлюдий, комедий и трагедий, и сейчас время антракта. Кушать подано — github.com/esosedi/regions
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 24
    • +1
      Сейчас начнётся про Крым :(
      • 0
        Написано очень эмоционально, но немного непонятно.

        Я могу воспользоваться этим сервисом и нарисовать границы районов Парижа, Франция на Google Maps?
        Или отобразить границы neighborhoods в San Francisco?

        Спасибо.
        • 0
          Париж? Это который №71525? Берем и показываем — jsfiddle.net/9o9ak7fb/24/
          Перевод в коллекции G или Y карт, и даже приведение в некий общий интерфейс тут есть.
          osmeRegions.geoJSON(71525/*Париж*/, {
                      lang: 'ru', 
                      quality:0,
                  }, function (data, pure) {
                      if(mtype=='Y'){
                          collection = osmeRegions.toYandex(data, ymaps);
                          collection.add(geoMap);    
                          geoMap.setBounds(collection.collection.getBounds(), {duration: 300});
                      }else{
                          collection = osmeRegions.toGoogle(data);
                          collection.add(geoMap);    
                          Gzoom(geoMap,collection.collection);
                      }
                      
              });
          


          Соседи US-CA? Настраиваем схему и показываем — jsfiddle.net/9o9ak7fb/25/
          osmeRegions.geoJSON('US-CA', {
                      lang: 'en', 
                      quality:0,
                      scheme: {
                          165475:function (region){
          // Для 165475(калифорния) выборка всех соседей СанФранциско.
                          return region.hasBorderWith(396487) && region.osmId!=396487;
                      }
                      },
                      postFilter: function (region){
          // Оставляем только Калифорнию
                          return region.osmId==165475;
                      }
                  }
          
          • +1
            Спасибо.

            Еще вопрос — обязательно ли подключать Yandex Maps API? Он подключен везде в примерах на jsfiddle.

            Под San Francisco neighborhoods я подразумевал примерно такую карту (районы города):

            San Francisco neighborhoods

            Без больших названий районов, разумеется.
            Карта отсюда.
            • 0
              Яндекс Карты не обязательны, вот районов нужных нет, как и всех внутренностей Египта, половины Китая и тд. Ничто не идеально :(
              • 0
                В OSM отмечены данные районы как точки, а в Wiki прописано, что данные единицы не имеют четких границ и рекомендуется отмечать просто точкой. Потому думаю в большинстве случаев не будет их ни у кого, если только кто-то не нанесет их сам.
                Границы есть только для отношений с тэгом boundary=administrative + admin_level=*
                • 0
                  В моем случае есть еще два хитрый момента:
                  1. В данные попали не всегда верные файлы — например церковные приходы
                  2. В экспорт попали только те данные, которые смогли замкнуться в контур. Очень многие relations, к сожалению, не смогли.
                  3. За время подготовки данных многие relations изменились или были вообще стерты.
                  Сейчас есть заново обработанные данные, чуть более корректные. Но пока не в проде.
                  По районам что LA, что Подольска или Мытищ ситуация одинаковая — они вроде как есть, но не совсем публичные. При этом всегда можно взять геометрию улиц, и собрать из них районы, кварталы, зоны обслуживания почтовых отделений…
                  • 0
                    При этом всегда можно взять геометрию улиц, и собрать из них районы, кварталы, зоны обслуживания почтовых отделений…

                    Ну тут собственно получится все условно, с большой вероятностью конечно совпадет с реальными границами, но все же.

                    У нас есть похожаю выгрузка, в ней получилось 254505 объектов административных границ. Если верить taginfo, всего отношений-границ 286 987.

                    Сейчас есть заново обработанные данные, чуть более корректные.

                    Чем обрабатываете и на чем?
                    • 0
                      У меня в базе 195к обьектов которые смогли собраться хотя бы в один контур. Из них 20 тысяч ушли в «дубликаты» — в OSM 3 или 4 Парижа вложенных друг в друга, три Амстердама и тд…
                      Обрабатываю самописными ручками. В начале экспорт из planet файла, потом две стадии предобработки ways, потом сборка, потом связка с родителями, потом мержинг с внешними БД (geoNames, booking итд).
                      Потом понимаешь, что в NY у тебя находится в Бруклине, и таблице регионов у него admin_level=8, и совершенно не понятно почему, когда, кто виноват....
                      • 0
                        Посмотрел Париж, там не все так однозначно, да есть два Парижа, но они являются разными административными единицами в составе Франции, ИМХО не совсем корректно так сливать их.
                        Еще возник вопрос на примере NY, согласно вашей схеме (визуализации) у вас слились воедино NY город и NY штат, возможно для вашей задачи это не критично, но все же вводит в заблуждение.
                        • 0
                          NY и NewYorkCity — две большие разницы.
                          Один — OpenStreetMap 175905, GeoNames 7163824, Wikipedia ru: Нью-Йорк
                          Второй — OpenStreetMap 61320, GeoNames 5128638, Wikipedia ru: Нью-Йорк (штат)
                          Да — называются одинаково, но все так и есть. И тот, который имеет ISO3166-2 код US-NY — штат.

                          С Парижем все хитрее — не стоит забывать, что базу в общем случае делал для себя. И для «пользователей».
                          Если человек три раза тыкает мышкой и не может попасть «ниже» — это не совсем правильно.
                          В итоге — в экспорте «поID» есть все регионы, и у всех есть полный перечень их «родителей» (ну почти*). Но вниз спускается хитро:
                          Для «стран» — спуск до прямых детей, и до регионов второго уровня. Так в России есть и ФО(дети) и Регионы(ISO). Тоже самое с Крымом — он вообще геометрически в контуре Украины. Но по ISO — в России.
                          В «Парижах» примерно тоже самое — под детьми подразумеваются те, чей родитель вы, или ваш «клон».

                          (ну почти*) вот в Париже поле parents у райнов или пустое, или кривое, сейчас поправим :(
          • 0
            О, поскольку вы в теме, хочу спросить, нет или в Яндексе, Гугле, или еще где либо возможности подсветить административные единицы уровня района или области? Не так, чтобы я вручную создал пару сотен или тысяч полигонов, а автоматического деления и команд вроде highlightRegion('Crimea')? Мне нужно вывести статистику по районам, то есть подсветить его, и напечатать поверх некоторые цифры.
            • 0
              Вы не поверите — именно для этого данная чтука и создана. В каждый файл входит как контур запрашиваемого обьекта, так и контура его подразбиений. Плюс вся нужная информация — кто кому родитель, ссылки на википедию и тд и тп.
              Надо просто знать где этот крым — 72639 или 'RU-CR'. По этим именам и он доступен.
            • 0
              А саму базу опубликовать не собираетесь?
              Раз уж ваши данные основаны на OSM, то как я понимаю,
              в соответствии с лицензией ODbL www.openstreetmap.org/copyright
              пункт 4.6 Access to Derivative Databases вы обязаны выложить ее в открытый доступ.
              • 0
                На гитхабе лежит github.com/theKashey/osme, который может собрать «такую» базу данных. В принципе этого достаточно для соблюдения условий дериватива «как базы данных». Но данный сервис никаким боком не БД, а обычный Produced Work, те на него распространяется пункт 4.3
                А вообще если OSM был бы не таким манерным и сам бы замержился с тем же GeoNames — мир стал бы лучше. Но нет, нельзя.....
                • 0

                  А где сейчас лежит osme, если ещё где-то лежит, конечно?

                  • 0
                    За прошедшие два года я успел: допилить геокодер, переписать регионы на es6, обзавестись третим сыном и переехать в Австралию.
                    Чего не успел — добиться стабильной сборки геометрии одной командой с консоли.
                    А osme некоторое время назад пришлось скрыть из-за некоторых моментов, которые не очень дружат с опенсорсом.
                    В данный момент переписываю. В моих интересах выложить его куда либо, чтобы получить помощь других разработчиков и снять это ярмо(tech debt) с шеи.
              • +1
                Долго пытался это к LeafLet применить, в итоге вот простой пример:

                osmeRegions.geoJSON('ru', {
                  lang: 'ru', 
                  quality:2,
                  type: 'coast'
                  }, function (data, pure) {
                      var geojson = new L.geoJson(data).addTo(map);
                });
                
                • 0
                  Думаю стоит добавить его в примеры. Эх, еще бы кто интерактивность для Леафлета докрутил…
                  • 0
                    Пока вы тут и быстро реагируете, задам вопрос.
                    Как использовать osmeRegions на локальном хосте, без интернета?
                    С учётом того, что ответ с data.esosedi.org закеширован.
                    Не хочется лезть в чужой код, но вижу что производятся какие-то преобразования ответа, в чистом виде он совсем не GeoJSON.
                    • 0
                      Формат там достаточно упоротый, и по сути нужен только для передачи данных.
                      Вариантов использования на локалхосте парочка:
                      1. Сохранить себе geoJSON. В принципе из .geoJSON он выходит в чистом виде.
                      2. Сохранить себе «оригинальные данные», парсить в geoJSON через osmeRegions.parseData.
                      3. Изменить хост (.setHost) или функцию .loadData так, чтобы ходить в свою проксирующую «ручку».
                  • 0
                    И тут же, возникла проблема с 180м меридианом, которая решается патчем к regions.js:

                    Патч
                    # This patch file was generated by NetBeans IDE
                    # It uses platform neutral UTF-8 encoding and \n newlines.
                    --- a/<html>regions.js (<b>12.10.2015 14:40:57</b>)</html>
                    +++ b/<html><b>Текущий файл</b></html>
                    @@ -93,6 +93,10 @@
                    
                                 while (index < byteVectorLength) {
                                     var position = [clampy(read() * fx + bounds[0][0]), clampx(read() * fy + bounds[0][1])];
                    +                if (position[1]<0) {
                    +                    position[1] = 360+position[1];
                    +                }
                                     result.push([position[1], position[0]]);
                                 }
                                 return result;

                    • 0
                      Это не правильное решение, так как вводятся "unbounded" координаты, те что-то запределами -180:180. Яндекс.Карты, например их не понимают. Google Maps вообще тоже — такой формат координат в принципе недопустим в geojson.
                      Суть в отсутствие алгоритма shortestPath в Лефлете, и это проблема Леафлета. В том числе он тут не совсем корректно воспроизведен, в том числе потому что должен работать уже после проекции.

                      Имеет смысл произвести специальную обработку координат перед тем как отдать их в L, раз ему нужно. Но не будет правильным решением впиливать это в основной блок декодирования.
                      • 0
                        "Правильный" shortestPath

                        function getShortestPath (contour) {
                            var halfWorld = 180;
                            var result = [contour[0]], point = contour[0];
                            for (var i = 1, l = contour.length; i < l; ++i) {
                                var delta = point[1] - contour[i][1];
                                if (Math.abs(delta) > halfWorld) {
                                    delta = delta < 0 ? -360:360;
                                } else {
                                    delta = 0;
                                }
                        
                                var nextPoint = [contour[i][0], contour[i][1] + delta]
                                result.push(nextPoint);
                                point = nextPoint;
                            }
                            return result;
                        }

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.