Архив рубрики: карта

Раскрасим домики

Итак, после предыдущих упражнений здания на нашей карте обзавелись номерами и высотой. Что дальше? Дальше можно, например, раскрасить здания в разные цвета. Можно раскрасить дома в зависимости от основного их предназначения (то есть, в зависимости от содержимого тэгов amenity и building). Можно — в зависимости от принадлежности здания.

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

Достичь подобного результат с картами OpenStreetMap, рисуемыми через библиотеку Leaflet, можно разными путями: можно, взяв какую-либо карту, не трогать её растровый слой, оставить его без изменений, добавив свой векторный слой, куда нанести контуры зданий. Пример — usjeans.ru/map. Другой подход — сделать собственный растровый слой, на котором сразу и отметить необходимое. Так как предыдущие эксперименты касались как раз собственного растрового слоя — продолжим опыты в том же направлении.

Отметить принадлежность зданий в OpenStreetMap можно разными путями: первый, очевидный — задать у каждого нужного нам здания какой-нибудь новый тэг. Тэг может быть практически любым — даже при существующих ограничениях на имена можно придумать что-нибудь. То множество тэгов, что применяется в OSM — результат не жёсткого диктата разработчиков, а договорённости сообщества участников и множество это легко расширяемо. Другой путь, который мне представляется более верным — создать отношение. Для группировки объектов, представляющих собой что-то общее, предназначено отношение с типом site. Как выяснилось, нужное мне отношение уже́ существовало — надо было лишь проверить его корректность и внести необходимые изменения (например, добавить здания, в отношение не попавшие).

На следующем этапе, импорте данных, выяснилось, что в imposm не предусмотрен способ сохранить в базе данных информацию о принадлежности к отношению. Во всяком случае, в документации он не описан. Не беда — эту информацию можно внести и после импорта, напрямую обратившись к базе данных. Для хранения информации о принадлежности нужно создать дополнительное поле. Так как нам нужен только признак принадлежности здания конкретному отношению, достаточно будет типа Bool. После всех изменений описание сопоставления данных для зданий в файле imposm-mapping.py стало выглядеть так:

buildings = Polygons(
    name = 'buildings',
    fields = (
        ('area', PseudoArea()),
        ('addr:housenumber',String()),
        ('building:levels',Integer()),
        ('height',Integer()),
        ('belongs_to_susu',Bool()),
    ),
    mapping = {
        'building': (
            '__any__',
        ),
        'railway': (
            'station',
        ),
        'aeroway': (
            'terminal',
        ),
    }
)

Выставить флаг принадлежности можно выполнением SQL-запроса

UPDATE osm_buildings
SET belongs_to_susu=1
WHERE osm_id IN (список_id, через запятую);

Чтоб не перечислять объекты вручную, был написан перловый скрипт. Попутно выяснилось, что, несмотря на то, что отношение может в себе содержать другие отношения, нет смысла устраивать рекурсивный обход: в поле osm_buildings.osm_id хранится первый попавшийся номер: он может быть как номером линии (для большинства зданий), так и номером отношения.

Задача определения роли здания после всех предыдущих манипуляций кажется не такой уж и сложной: во-первых, тип здания (точнее, его предназначение) может хранится в тэге building — его содержимое при импорте попадает в поле osm_buildings.type. Среди допустимых типов есть и dormitory — общежития (кстати, не надо путать с tourism:hostel — это совсем разные тэги). Тип «учебные корпуса» (например, academic_building) среди часто применяемых не встречается — есть лишь university, который не совсем подходит: университет — это не только учебные корпуса. Можно, конечно, прямо у самих зданий указывать building=academic, но мне более правильным показался вариант указания роли здания внутри отношения — там и поле для роли есть, и оно не используется совсем. Больше шансов, что данные спокойно сохранятся именно тогда, когда они лежат внутри отношения. А вот в базу данных роль вносится в поле osm_buildings.type:

UPDATE osm_buildings
SET type='academic'
WHERE osm_id IN (список_id, через, запятую);

Как и прежде, для упрощения назначения ролей, написан ещё один перловый скрипт.

Стилевые правила для зданий (в OSM Bright он лежат в файле base.mss) начинаются так:

#buildings[zoom>=12] {
  polygon-fill:@building;
  // Our buildings
  [belongs_to_susu=1] {
    polygon-fill: @susu_building;
    [type='dormitory'] {
     polygon-fill: @susu_dormitory;
    }
    [type='academic'] {
     polygon-fill: @susu_academic;
    }
  }

Определение цветов @susu_building, @susu_dormitory, @susu_academic добавлено в файл palette.mss — там для них самое подходящее место.

@susu_building:     saturate(darken(@building, 15%), 10);
@susu_dormitory:    mix(@susu_building, #f60, 95%);
@susu_academic:     mix(@susu_building, #06f, 93%);

Результат:
Карта с разноцветными домами

Больше этажей!

Продолжаем эксперименты с обработкой карт напильником. В прошлый раз удалось добавить на карту номера домов — сегодня попытаемся сделать дома чуть выше.

С одной стороны в CartoCSS есть свойство building-height, описывающее «высоту» зданий — это видно на крупных масштабах (от 17). В OSM Bright величина building-height постоянна, что даёт на карте здания одинаковой высоты.

Карта

С другой стороны, в OpenStreetMap можно хранить данные о высоте различных зданий с сооружений: для этого предназначены тэги height (высота, по умолчанию в метрах) и building:levels (количество этажей). И эти, взятые из OSM, сведения о высоте либо числе этажей вполне можно использовать для building-height — там можно указывать не только числа, но и выражения.

Задача по добавлению поля с высотами в PostGIS аналогична предыдущей — добавлению номеров домов. Надо добавить в imposm-mapping.py пару полей — building:levels и height типа Integer. Я не нашёл способ, как сразу, на этапе импорта данных, выбирать нужное поле, то у которого есть хоть какое-то значение — это не страшно, можно выбрать позже.

Аналогично предыдущей выполняется и задача добавления данных в TileMill: надо добавить новые поля в SQL-запрос в настройках слоя #buildings.

Остаётся последнее — написать подходящее выражение для высоты. В отличие от использования данных в подписях, где имя поля надо было заключить и в квадратные скобки, и в кавычки, здесь, при вычислении выражений, кавычки не нужны — нужны лишь квадратные скобки:

building-height: 1 + [building:levels];

Как выбрать одно из двух полей? В выражении нельзя использовать операцию || — можно лишь or, но она в качестве результата выдаёт true либо false. Можно сделать так: сначала указать высоту, основываясь на приблизительном источнике (на числе этажей), а затем, при наличии более точного (собственно, высоты), взять данные с него. В CartoCSS, как и в обычном CSS, при одинаковом приоритете правил срабатывает то, что было объявлено последним.

Правила для указания высоты зданий на карте будут выглядеть так:

building-height: 1 + [building:levels];
[height>0] {
    building-height: 1 + [height] / 3;
}

Кроме того, лучше передвинуть слой зданий #buildings повыше, чтоб здания располагались над дорогами.

Результат:

Карта OSM со зданиями разной высоты

В следующий раз мы рассмотрим способ раскраски зданий в разные цвета.

Как добавить номера домов на карту

На карте, отрисовываемой из OpenStreetMap стилем, основанным на OSM Bright, нет номеров домов. Попытки на скорю руку сочинить какой-нибудь стиль, заглядывая в нагугленное, не увенчались успехом — пришлось разобраться подробнее. Выяснилось следующее: одним редактированием стилевого файла не обойтись, надо ещё и внести данные в базу.

Итак, есть PostgreSQL, PostGIS, imposm, TileMill и OSM Bright. Как запустить этот комбайн, написано, например, на mapbox.com/tilemill/docs/guides/osm-bright-ubuntu-quickstart/. Пробуем, запускаем — номеров домов нет. Если посмотреть, какие данные, относящиеся к зданиям, хранятся в базе данных, то увидим, что там нет адресов:

$ psql -U юзер -d база -c '\d osm_buildings'

                                  Table "public.osm_buildings"
  Column  |          Type          |                         Modifiers                          
----------+------------------------+------------------------------------------------------------
 id       | integer                | not null default nextval('osm_buildings_id_seq'::regclass)
 osm_id   | bigint                 | 
 name     | character varying(255) | 
 type     | character varying(255) | 
 area     | real                   | 
 geometry | geometry               | 
Indexes:
    "osm_buildings_pkey" PRIMARY KEY, btree (id)
    "osm_buildings_geom" gist (geometry) CLUSTER
Check constraints:
    "enforce_dims_geometry" CHECK (st_ndims(geometry) = 2)
    "enforce_srid_geometry" CHECK (st_srid(geometry) = 900913)

Чтоб сохранять адрес (а точнее, номер дома — нам этого достаточно), надо добавить строковое поле (точнее, это будет VARCHAR(255)) с именем 'addr:housenumber' к таблице с зданиями и заполнить это поле номерами. Сделать это можно, указав его в файле, где хранятся правила соответствия (в OSM Bright такой файл называется imposm-mapping.py):

--- a/imposm-mapping.py
+++ b/imposm-mapping.py
@@ -143,6 +143,7 @@ buildings = Polygons(
     name = 'buildings',
     fields = (
         ('area', PseudoArea()),
+        ('addr:housenumber',String()),
     ),
     mapping = {
         'building': (

После этого можно запускать импорт, в получившейся таблице появится поле addr:housenumber, содержащее номера домов.

Следующий этап — указать новое поле в настройках слоя TileMill в поле Table or subquery:

Настройка слоя buildings

Содержимое поля должно быть таким:

( SELECT geometry, type, name, area,"addr:housenumber"
    FROM osm_buildings
  ORDER BY ST_YMin(ST_Envelope(geometry)) DESC
) AS data

После чего останется добавить стиль номеров в файл labels.mss:

#buildings[zoom>16] {
  text-name:'[addr:housenumber]';
  text-face-name:@sans;
  text-size:9;
  text-fill: lighten(@poi_text, 20%);
}

Результат — susu.ac.ru/ru/about/campus

Картографические данные © Участники OpenStreetMap http://osm.org/

Пока в городе дзюдо, проще ездить на велосипеде

Аргументы и факты — Челябинск: На время проведения ЧМ по дзюдо в Челябинске вводится ограничение движения:

Улицы северо-запада и центра Челябинска будут перекрывать в связи с проведением в столице Южного Урала Чемпионата мира по дзюдо, сообщили в городском ГИБДД.

Ограничение движения в разное время коснется улиц Труда, Северокрымской, Братьев Кашириных, Салавата Юлаева, Воровского, Доватора, Энгельса, Молодогвардейцев, Лесопарковой, Худякова, Чичерина, 250 лет Челябинска, Комсомольского проспекта, проспекта Ленина, Красной.

Конкретные даты и места не указаны. Если отметить все перечисленные улицы, получится так:

[map]55.16821,61.42838 55.16784,61.41566 55.1681,61.41411 55.16834,61.41342 55.16752,61.38973(red|); 55.1677,61.38823 55.16767,61.38066 55.17069,61.37107 55.17098,61.36847 55.17091,61.35823 55.17871,61.35813(red|); 55.16835,61.41345 55.169,61.41248 55.17188,61.41113 55.17359,61.4048 55.17339,61.39808 55.17438,61.39205 55.17409,61.38482 55.17447,61.38218 55.17823,61.37244 55.17868,61.37002 55.17872,61.31835 55.17783,61.31414 55.16985,61.29639 55.16873,61.29517 55.16715,61.29485 55.16279,61.29493(red|); 55.16717,61.29932 55.16783,61.29928 55.17992,61.28303(red|); 55.17438,61.33568 55.17652,61.33304 55.17746,61.33268 55.19733,61.33259 55.19828,61.33231 55.19856,61.33206 55.20793,61.31803(red|); 55.15869,61.47471 55.1612,61.43372 55.16035,61.40121 55.16021,61.40031 55.1593,61.3645 55.14737,61.36532(red|); 55.13736,61.40948 55.13801,61.40205 55.13845,61.40031 55.14771,61.37961 55.14668,61.33504 55.14683,61.33287 55.14728,61.32976(red|); 55.14773,61.37959 55.1486,61.38064 55.15252,61.38044 55.1529,61.38072 55.15317,61.38137 55.15321,61.3818 55.17051,61.38038(red|); 55.13629,61.3751 55.14127,61.37514 55.16028,61.40079(red|); 55.17033,61.3091 55.19103,61.28084(red|); 55.15524,61.394 55.16997,61.3928(red|); 55.16004,61.28577 55.17191,61.28543 55.17303,61.28689 55.182,61.30753 55.18188,61.31543(red|); 55.18698,61.28646 55.19408,61.30221 55.1945,61.30414 55.19447,61.35439 55.19183,61.36349 55.1922,61.39053(red|)[/map]

Пятница пройдёт в пробках

На полдня в рабочий день (и пятничный вечер захватят) перекроют дороги, сообщает Ura.ru, и даже пешеходам житья не будет:

В пятницу, 6 июня, во время проведения Всероссийского форума «ЖКХ — новое качество», будет временно ограничено движение транспорта и пешеходов на десяти улицах в разных районах Челябинска …

«С 14.00 до 19.00 возможно временное ограничение движения транспорта и пешеходов на следующих улицах города: Бродокалмакский тракт, Героев Танкограда, Лесопарковая, Бажова, 250 лет Челябинску, проспект Комарова, Салавата Юлаева, проспект Ленина, Худякова и Университетская Набережная» …

Напомним, участники форума начнут съезжаться на Южный Урал в четверг, 5 июня. Уже на следующий день им придется подняться в семь, чтобы с 8 до 9 утра собраться в гостинице «Рэдиссон Блу». … Для мероприятий … в Челябинске избраны площадки на базе гостиницы «Рэдиссон Блу», завода «Прибор», ДК «Железнодорожников», техникума городского хозяйства и Ледовой арены «Трактор».

Перечисленные улицы и учреждения отмечены синим. Красным — наиболее вероятный маршрут.

[map]55.16739,61.37907; 55.15258,61.40849; 55.17446,61.28706; 55.19216,61.35742; 55.11933,61.45572; 55.23641,61.52807 55.23551,61.51777 55.23181,61.50704 55.22746,61.49863 55.22626,61.4973 55.22469,61.49662 55.22129,61.4958 55.21945,61.49468 55.21637,61.49014 55.2127,61.48803 55.20621,61.47829 55.20114,61.47318 55.1906,61.4646 55.18759,61.46632 55.1711,61.45331 55.16012,61.4513 55.16124,61.43387 55.15933,61.36439 55.14734,61.36542 55.14665,61.33465 55.14767,61.32706 55.14814,61.3262 55.14862,61.3212 55.14912,61.31974 55.1497,61.31903 55.15048,61.31877 55.15255,61.3197 55.15366,61.31933 55.15467,61.31695 55.1564,61.30807 55.15992,61.30244 55.1672,61.30234(red|); 55.173,61.28702 55.17443,61.29028 55.16715,61.30015(red|); 55.15862,61.47666 55.16014,61.45151; 55.17911,61.43641 55.18401,61.45864 55.19058,61.46439; 55.17036,61.4531 55.19714,61.4358 55.21311,61.44207 55.2162,61.44233 55.21615,61.44353 55.2207,61.44422 55.23568,61.42499; 55.14739,61.36559 55.14771,61.37959 55.14651,61.38237; 55.17911,61.28406 55.17451,61.29036 55.18191,61.30749 55.18183,61.31611; 55.16715,61.30268 55.17289,61.31495 55.17406,61.31933 55.17436,61.32302 55.17465,61.35487 55.17563,61.35967 55.17568,61.37246; 55.15995,61.28573 55.17201,61.28551 55.173,61.28689[/map]

Вот зачем нужно это показушное катание по центру города? По такому вот «гостевому маршруту» — зачем? Могли бы по проспекту Победы быстренько довезти, через старые дома да деревню, да многоэтажки панельные.

Трамвайная схема из OpenStreetMap

Когда-то для статьи «Челябинский трамвай» в википедии я нарисовал поверх какой-то карты схему маршутов, простенькую, куда проще бирмановской. Нарисовал, выложил нарисованное (без карты, только схему) в векторном виде, да и забыл — пить-есть не просит.

Прошло шесть с половиной лет, OpenStreetMap развился до состояния, когда на карты некоторых районов уже можно стало смотреть без слёз. «Почему б не попробовать схему с картой из OSM?» — подумал я. И попробовал.

Сначала был TileMill (я год назад о нём рассказывал на UWDC) — можно, например, сделать свой картостиль, основываясь на OSM Bright. Карта получается достаточно симпатичной и её внешний легко настраивается — в тайлмилле используется CSS-подобный язык разметки. Однако добавить на карту трамвайные маршруты не получилось — в OSM Bright трамвайные пути и железные дороги отображаются совершенно одинаково. Это, наверное, можно изменить, поковырявшись в настройке, но скрипты преобразования геоданных написаны на питоне, в котором я почти ничего не понимаю. Поэтому пришлось добавлять трамвайные линии вручную. Результат —

Попробовал альтернативный способ — alaCarte. В alaCarte оказалось возможным сразу выделить трамвайные пути, и, кроме того, alaCarte создаёт набор тайлов, пригодный для использования с библиотеками OpenLayers и Leaflet.

Получается, например, так:

Схема трамвайных маршрутов

в виде интерактивной карты — http://tile.susu.ac.ru/tram.html, используемые стили скоро будут на гитхабе. Stay tuned!