В комментариях в одной из прошлых статей и на гитхаб писали что не обязательно жить близко к магазину, когда есть службы доставки. Вот с этим я могу поспорить - большая часть людей в России покупает продукты и товары в магазинах. В 90е были крайне популярны вещевые рынки и часто покупали одежду там потому что было дешевле. У многих студентов и пенсионеров их финансовая ситуация не располагает не то что платить за доставку курьером, но даже возможность купить хорошие продукты и одежду после квартплаты - это арифметика жизни. Им неосознанно приходится решать задачу коммивояжер�� в голове чтобы обойти несколько торговых точек и выгодно купить по заранее составленному списку покупок. Шаговая доступность магазинов все еще важна для жителей городов.
Как выбрать правильное место для открытия магазина и что нужно учесть
Его локация должна быть в удобном месте для потенциальных клиентов. Рядом с офисными зданиями, в туристическом или в жилом районе. Учитываются поток пешеходов, так и доступность общественного транспорта и парковок. И конечно учитывают целевую аудиторию: для магазинов детских товаров, лучше расположить в том районе где много семей с детьми и инфраструктуры для них.
Торговля требует выполнения стандартов безопасности и санитарии. Нужно быть уверенным что здание соответствует требованиям пожарной безопасности, санитарии и перепланировки торгового помещения узаконены. Помещение должно иметь достаточно места для товара и посетителей магазина. Хорошо бы найти профессионального юриста / агента по коммерческой недвижимости для помощи при заключения договора и решении юридических вопросов связанных с торговым помещением. Рекомендации + опыт эксперта поможет избежать лишних трат и распространенных ошибок при выборе здания.
Помимо арендной платы или стоимости покупки помещения, нужно заложить дополнительные расходы на ремонт, обслуживание помещений, зарплаты персонала, закупку товара и уплату налогов.
Как много в этом районе конкурентов? Кто целевая аудитория, где проживает или как часто посещает этот район?
Можно ли адаптировать здание при изменениях в бизнес-модели. Например решите сменить категорию товаров и что нужно будет учесть, что прийдется менять в торговом зале...
И это не все, а мы же сфокусируемся только на расположении и анализе пешеходной доступности магазинов и попрактикуемся в решении задачи - сколько человек сможет дойти до конкретного магазина: обычно люди предпочитаю покупать там где магазин ближе и где больше ассортимент товаров.
Специалисты, кто занимаются геоаналитикой профессионально для открытия новых магазинов, имеют доступ к большому объему часто платной информации: локации абонентов от операторов связи, платежных операторов и банков, доступ к подготовленным данным из ГИС ЖКХ/Реформа ЖКХ, и много еще к чему. Но они, как почтальон Печкин, вам их не дадут. И не дадут результат расчета на данных: дешево, быстро, качественно - выбирай любые два варианта.
Не претендую на академичность и точность моего субъективного исследования, лишь покажу вам как cамостоятельно рассчитать достижимость магазинов клиентами на основе свободных данных из OpenStreetMap. К счастью, для Москвы полнота и качество данных для многоэтажных жилых домов одно из самых лучших по РФ.
Данные и расчеты
Как подготовть данные для анализа я уже рассказывал в "Где 15 минут пешком от дома до метро в Москве…" и в "Где бы вы точно не жили и не остановились даже на время, если бы знали и выбирали на основе фактов". Пример расчета расстояний для магазинов с программой также публиковал в статье "Жилье в 500м от сетевых продуктовых магазинов в Москве". Данные из OpenStreetMap PBF файла загружаются в PostgreSQL базу с установленным расширением PostGIS и H3.
Кроме пешеходных дистанций, расчитаных с помощью GraphHopper, понадобится еще число квартир в каждом доме. Его можно найти в значениях OSM тега building:flats.
Для жилых домов, у которых не указан этот тег проставляем число квартир по правилам:
Если для здания указан диапазон квартир на подъездах (
addr:flats) то выбираем максимальное значение для дома. Если это значение отличается не более чем на 50% от вычисленного по среднему, то для здания выбираем его, иначе значение из следующего пунктаможно проставить среднее значение квартир и умножить на число этажей в доме и площадь здания:
CREATE TABLE flats_per_building AS WITH flats_precalc AS (SELECT g.id, g.type, (CASE WHEN g.tags->'building:levels' ~ '^\d+(\.\d+)?$' THEN g.tags->'building:levels' ELSE NULL END)::real levels, (CASE WHEN g.tags->'building:flats' ~ '^[0-9]+$' THEN g.tags->'building:flats' ELSE NULL END)::smallint flats, (CASE WHEN g.type='ways' AND ST_IsClosed(g.geom) THEN st_area(ST_MakePolygon(g.geom)::geography) WHEN g.type='multipolygon' THEN st_area(g.geom::geography) ELSE 0 END) area FROM geometry_global_view g WHERE type<>'nodes' AND (CASE WHEN g.tags->'building:levels' ~ '^\d+(\.\d+)?$' THEN g.tags->'building:levels' ELSE NULL END)::real>2 --and **проверка что здание жилое - портянка OSM тегов**; ), building_max_flat AS (SELECT g.id, type, max(max_flat)::smallint max_flat FROM geometry_global_view g INNER JOIN (SELECT id,(regexp_split_to_array(tags->'addr:flats', '-'))[2] max_flat, geom FROM nodes WHERE tags?'addr:flats' AND tags?'entrance' AND tags->'addr:flats' ~ '^\d+\-\d+?$') e ON st_contains(CASE WHEN TYPE='ways' AND ST_IsClosed(g.geom) THEN ST_MakePolygon(g.geom) ELSE g.geom END, e.geom) WHERE type<>'nodes' AND ((CASE WHEN g.tags->'building:levels' ~ '^\d+(\.\d+)?$' THEN g.tags->'building:levels' ELSE NULL END)::real>2) AND not(g.tags?'building:flats') GROUP BY 1,2), flats_aprox AS (SELECT id, type, flats, (levels*area* (SELECT avg((flats/levels)/area) FROM flats_precalc WHERE area>0 AND flats>0))::smallint aproximated_flats FROM flats_precalc) SELECT b.id, b.type, coalesce(flats, CASE WHEN max_flat IS NOT NULL AND aproximated_flats>0 AND abs(max_flat-aproximated_flats)/(aproximated_flats::real)<0.5 THEN -1*max_flat ELSE aproximated_flats END) flats FROM flats_aprox b LEFT JOIN building_max_flat USING(id,type); ALTER TABLE flats_per_building ADD PRIMARY KEY(id,type);
Спасибо @roman_deev за подсказку в комментарии к статье, чтобы получить более точное решение по числу квартир из данных уже доступных в OSM!
Простой вариант того же запроса, где отсутствующие flats заполняются только средним
create table flats_per_building as WITH flats_precalc as (select g.id,g.type, (case when g.tags->'building:levels' ~ '^\d+(\.\d+)?$' then g.tags->'building:levels' else NULL end)::real levels, (case when g.tags->'building:flats' ~ '^[0-9]+$' then g.tags->'building:flats' else NULL end)::smallint flats, (CASE WHEN g.type='ways' and ST_IsClosed(g.geom) THEN st_area(ST_MakePolygon(g.geom)::geography) WHEN g.type='multipolygon' THEN st_area(g.geom::geography) ELSE 0 END) area from geometry_global_view g where type<>'nodes' and (case when g.tags->'building:levels' ~ '^\d+(\.\d+)?$' then g.tags->'building:levels' else NULL end)::real>2)--and **проверка что здание жилое - портянка OSM тегов**; select id,type, coalesce(flats, (levels*area* (select avg((flats/levels)/area) from flats_precalc where area>0 and flats>0))::smallint) flats from flats_precalc ; alter table flats_per_building add primary key(id,type);
Это простое решение чтобы заполнить пропуски в данных. Правильнее и точнее - это обойти дома где не указано число квартир и по почтовым ящикам( табличкам с номером подъезда) посмотреть номера квартир и указать точное число квартир в доме в исходных данных. Когда анализ выполняется для больших территорий, то можно найти данные в ГИС ЖКХ, но выгрузок для этого сайта в открытом доступе нет.

Итак, у нас есть дистанции расчитаные от многоэтажных жилых домов до магазинов. Если просуммировать число квартир в радиусе 1км от магазина, то это число будет пропорционально числу людей которые смогут прийти в этот магазин.
osmworld=# \d flats_per_building Column | Type | --------+-----------------| id | bigint | type | table_reference | flats | smallint | Indexes: "flats_per_building_pkey" PRIMARY KEY, btree (id, type) osmworld=# \d distance Column | Type | ---------------+-----------------| building_id | bigint | building_type | table_reference | poi_id | bigint | poi_type | table_reference | distance | smallint | osmworld=# select count(*) from distance; count ---------- 60816611 (1 row) Time: 1722,807 ms (00:01,723)
Теперь объединим эти данные - сложив число квартир каждого дома в пешеходной доступности 1000м от точек интереса (POI) - магазинов:
create table flats_per_poi as select poi_id, poi_type, sum(flats) total_flats from distance inner join flats_per_building on building_id=id and building_type=type and distance<=1000 group by poi_id,poi_type order by total_flats desc; SELECT 129128 Time: 3108,646 ms (00:03,109)
Обогатим эти данные районом и в каждом районе отсортируем данные по убыванию числа квартир в пешей доступности:
CREATE TABLE moscow_district AS select tags->'name' name,polygon from multipolygon where tags->'admin_level'='8' and tags->'boundary'='administrative'; CREATE INDEX idx_moscow_district_geometry ON moscow_district USING gist (polygon); create table best_retail_location_per_district as select name, row_number() over (partition by district order by total_flats desc) max_flats_rating, district, total_flats, centre from (select total_flats, coalesce(r.tags->'name',r.tags->'name:ru',r.tags->'name:en') name, (select name from moscow_district where st_contains(polygon, centre) limit 1) district, centre from flats_per_poi inner join poi r on poi_id=id and poi_type=type and 'shop' = any(categories) and coalesce(r.tags->'name',r.tags->'name:ru',r.tags->'name:en') is not null order by total_flats desc) shops;
Если нужен анализ конкурентов при открытии продуктового магазина или места для торговли товаром который не представлен у сетевых магазинов Пятёрочка, Перекрёсток, Дикси, Магнит, Магнолия, ВкусВилл, Лента, Азбука Вкуса, Ашан, Атак, но попадает в ту же целевую аудиторию, можно переиспользовать аналитику которую уже для себя выполнили торговые сети и открыли в нужном месте магазин. Для этого в фильтры можно добавить:
(tags@>'shop=>supermarket' or tags@>'shop=>convenience') and (tags->'brand' in ('Billa', 'Eurospar','EuroSPAR','EUROSPAR', '"METRO Cash & Carry"', 'Spar','SPAR', '"АВ Daily"','Авокадо','Авоська','"Азбука Вкуса"', 'Атак','Ашан','"Ашан Сити"', 'Верный','Виктория', 'ВкусВилл','Дикси','Лента','Магнит', 'Магнолия','"Мини Лента"','МясновЪ', 'О’КЕЙ', 'Перекрёсток','"Перекресток Экспресс"', 'Пятёрочка','Светофор', '"Супер Лента"','СуперЛента', '"У Палыча"','Фасоль','Чижик','Ярче!')
Статистика по брендам продуктовых в Москве:
select upper(tags->'brand') brand,count(*) from geometry_global_view where (tags@>'shop=>supermarket' or tags@>'shop=>convenience') and tags?'brand' group by 1 having count(*)>1 order by 2 desc; brand | count ----------------------+------- ПЯТЁРОЧКА | 1144 ВКУСВИЛЛ | 596 ДИКСИ | 432 МАГНИТ | 391 ПЕРЕКРЁСТОК | 296 МАГНОЛИЯ | 161 ЛЕНТА | 82 У ПАЛЫЧА | 76 ВЕРНЫЙ | 69 АЗБУКА ВКУСА | 68 МЯСНОВЪ | 56 СУШИ WOK | 39 EUROSPAR | 28 АВ DAILY | 27 АШАН | 27 СУПЕР ЛЕНТА | 24 ВИКТОРИЯ | 24 ЯРЧЕ! | 22 АТАК | 15 SPAR | 11 СВЕТОФОР | 11 О’КЕЙ | 9 ЧИЖИК | 9 АВОКАДО | 6 ПЕРЕКРЕСТОК ЭКСПРЕСС | 6 АШАН СИТИ | 3 МИНИ ЛЕНТА | 3 АВОСЬКА | 3 METRO CASH & CARRY | 3 ФАСОЛЬ | 3 НЕФТЬМАГИСТРАЛЬ | 2 BILLA | 2 ПРОДУКТОВИЙ МАГАЗИН | 2 (33 rows)
Отобразим в каждом районе лучшие места для торговых точек по числу жителей:

Список существующих торговых мест по одному в каждом из районов Москвы с наибольшим количеством квартир в пешеходных окрестностях 1км от торгового помещения:
select district, total_flats, st_x(centre), st_y(centre) from best_retail_location_per_district where max_flats_rating=1 order by total_flats desc; district | total_flats | st_x | st_y ---------------------------------+-------------+--------------------+-------------------- район Митино | 44554 | 37.355865200000004 | 55.8478696 район Измайлово | 39255 | 37.800719 | 55.793489400000006 район Восточное Измайлово | 39254 | 37.799821800000004 | 55.79304380000001 район Гольяново | 32907 | 37.80093225 | 55.81096895 район Раменки | 31709 | 37.5055284 | 55.70210530000001 район Северное Измайлово | 30541 | 37.8124447 | 55.8050585 Ломоносовский район | 29928 | 37.5473658 | 55.6803493 район Южное Бутово | 29658 | 37.5593346 | 55.5486773 Таганский район | 29457 | 37.6752278 | 55.739144700000004 район Черёмушки | 28843 | 37.5469279 | 55.6735065 Пресненский район | 28788 | 37.518790700000004 | 55.7570794 Гагаринский район | 28615 | 37.5473967 | 55.6811439 район Хорошёво-Мнёвники | 28588 | 37.4602547 | 55.7818655 район Щукино | 28155 | 37.4625799 | 55.8089335 район Ховрино | 27974 | 37.5031309 | 55.8609803 Обручевский район | 27952 | 37.517463 | 55.6643959 район Аэропорт | 27551 | 37.5356024 | 55.803232200000004 район Северное Бутово | 27199 | 37.577473600000005 | 55.568533 район Некрасовка | 27009 | 37.927194300000004 | 55.7028795 Бескудниковский район | 26881 | 37.550535800000006 | 55.875935000000005 район Кунцево | 26791 | 37.410576407649934 | 55.73863922462011 район Богородское | 26623 | 37.719145000000005 | 55.8078323 район Зюзино | 26066 | 37.599057900000005 | 55.653555100000005 район Левобережный | 25818 | 37.475883100000004 | 55.864906600000005 район Северное Тушино | 25815 | 37.4244894 | 55.8499326 район Солнцево | 25813 | 37.4061808 | 55.6502106 район Коньково | 25808 | 37.524680000000004 | 55.6426711 район Восточное Дегунино | 25701 | 37.5481326 | 55.878368900000005 район Люблино | 25541 | 37.7584202 | 55.67367470000001 район Северное Медведково | 25425 | 37.662568300000004 | 55.888120150000006 Академический район | 25403 | 37.5539208 | 55.6836137 район Преображенское | 25278 | 37.717225500000005 | 55.804943400000006 район Выхино-Жулебино | 24985 | 37.8533144 | 55.684696200000005 район Марьино | 24825 | 37.765138 | 55.668098 Дмитровский район | 24682 | 37.5332311 | 55.8812342 район Арбат | 24663 | 37.5830846 | 55.7471182 район Южное Тушино | 24653 | 37.425816600000005 | 55.848617700000005 район Отрадное | 24578 | 37.60205305 | 55.863150100000006 район Новокосино | 24372 | 37.858880500000005 | 55.740321800000004 Нагорный район | 24369 | 37.6060573 | 55.6516913 Ново-Переделкино | 24264 | 37.349189800000005 | 55.6410172 район Текстильщики | 24168 | 37.7484052 | 55.7007545 район Нагатинский Затон | 24130 | 37.6775296 | 55.68217430000001 район Свиблово | 24093 | 37.6462869 | 55.8517595 район Хамовники | 24042 | 37.5789264 | 55.7254597 Тверской район | 24037 | 37.5973848 | 55.7709714 Алексеевский район | 23962 | 37.645125400000005 | 55.81044300000001 Орехово-Борисово Южное | 23737 | 37.7338043 | 55.6073353 район Новогиреево | 23715 | 37.8027832 | 55.753313600000006 Головинский район | 23674 | 37.5111866 | 55.85889400000001 район Чертаново Центральное | 23578 | 37.593337250000005 | 55.60662975 район Кузьминки | 23536 | 37.763281 | 55.705186100000006 район Перово | 23473 | 37.7999486 | 55.754013400000005 Лосиноостровский район | 23342 | 37.6858603 | 55.876531 район Бибирево | 23312 | 37.596387500000006 | 55.8903642 район Южное Медведково | 23006 | 37.6367825 | 55.87363860000001 Рязанский район | 22981 | 37.778901677736364 | 55.71208193761292 район Строгино | 22929 | 37.4063571 | 55.807376600000005 Тимирязевский район | 22852 | 37.5732797 | 55.812274200000004 район Якиманка | 22658 | 37.6136508 | 55.7213738 район Чертаново Южное | 22591 | 37.605664700000005 | 55.594416100000004 район Крылатское | 22568 | 37.4084348 | 55.760584400000006 район Проспект Вернадского | 22325 | 37.512944600000004 | 55.666986800000004 поселение Сосенское | 22225 | 37.477374700000006 | 55.57048090000001 Бабушкинский район | 21958 | 37.6771351 | 55.867305300000005 поселение Внуковское | 21831 | 37.3237201 | 55.6370195 район Зябликово | 21643 | 37.7416136 | 55.6214627 район Тёплый Стан | 21573 | 37.5181272 | 55.632703400000004 Даниловский район | 21467 | 37.6175062 | 55.719938000000006 район Ясенево | 21410 | 37.5439495 | 55.607119700000005 район Ивановское | 21390 | 37.826334100000004 | 55.753645600000006 район Коптево | 21371 | 37.520052 | 55.823584700000005 район Марьина Роща | 21313 | 37.6127702 | 55.7926579 район Лианозово | 21263 | 37.5785372 | 55.89771390000001 район Сокол | 21262 | 37.5147357 | 55.800178700000004 Останкинский район | 21086 | 37.637360900000004 | 55.8134049 Южнопортовый район | 21057 | 37.664825900000004 | 55.731036100000004 Савёловский район | 21011 | 37.5738591 | 55.7945739 район Замоскворечье | 20951 | 37.617964 | 55.7204885 Басманный район | 20784 | 37.6759708 | 55.77245370000001 район Орехово-Борисово Северное | 20782 | 37.708416400000004 | 55.609964500000004 район Западное Дегунино | 20547 | 37.5228794 | 55.8780158 район Чертаново Северное | 20459 | 37.6001775 | 55.6291803 Бутырский район | 20390 | 37.581405100000005 | 55.8199976 район Сокольники | 20328 | 37.676637400000004 | 55.7855112 Войковский район | 20192 | 37.5187881 | 55.821412200000005 Мещанский район | 20033 | 37.621846000000005 | 55.7732935 Можайский район | 19652 | 37.423553500000004 | 55.725824700000004 Донской район | 19509 | 37.607535000000006 | 55.7157616 район Очаково-Матвеевское | 19501 | 37.4757927 | 55.7104287 Тропарёво-Никулино | 19432 | 37.4681865 | 55.664694000000004 Красносельский район | 18394 | 37.636166200000005 | 55.7719856 Алтуфьевский район | 18364 | 37.597489200000005 | 55.885716900000006 район Лефортово | 18326 | 37.7081619 | 55.7505072 район Братеево | 18287 | 37.7634054 | 55.63621010000001 район Соколиная Гора | 18285 | 37.7326258 | 55.779113100000004 район Царицыно | 18200 | 37.6565437 | 55.6260485 Хорошёвский район | 17739 | 37.533148600000004 | 55.776608200000005 район Вешняки | 17409 | 37.823054400000004 | 55.721068200000005 район Котловка | 17388 | 37.5936012 | 55.6813876 район Косино-Ухтомский | 17371 | 37.88647929577547 | 55.7146019953649 район Дорогомилово | 17313 | 37.5489696 | 55.743085300000004 район Филёвский Парк | 17221 | 37.489751000000005 | 55.7397427 район Нагатино-Садовники | 17176 | 37.6629725 | 55.679679900000004 район Печатники | 17069 | 37.7247369 | 55.682193700000006 район Фили-Давыдково | 16830 | 37.451718400000004 | 55.723709500000005 район Беговой | 16642 | 37.5755901 | 55.7896471 район Ростокино | 15438 | 37.6557199 | 55.8335372 район Марфино | 14216 | 37.589894400000006 | 55.8285594 район Метрогородок | 13866 | 37.7586345 | 55.825178550000004 район Бирюлёво Восточное | 13122 | 37.66389045 | 55.601072300000006 район Москворечье-Сабурово | 12305 | 37.663324800000005 | 55.6403018 район Покровское-Стрешнево | 12231 | 37.4536358 | 55.830881500000004 Ярославский район | 10980 | 37.687691 | 55.862006400000006 поселение Московский | 10884 | 37.41915496649685 | 55.660058285851896 район Северный | 10731 | 37.544192200000005 | 55.928780100000004 Нижегородский район | 10346 | 37.681484632205915 | 55.72787293563479 поселение Воскресенское | 9373 | 37.514949 | 55.5256966 район Капотня | 9128 | 37.796075200000004 | 55.6350781 район Куркино | 7364 | 37.428532700000005 | 55.87720755000001 район Внуково | 5940 | 37.2621203 | 55.614216500000005 поселение «Мосрентген» | 4296 | 37.4733691 | 55.620890900000006 район Бирюлёво Западное | 1817 | 37.6464437 | 55.5913467
Где total_flats - число квартир в радиусе 1км от торговой точки района district и её координаты. Конечно эти цифры включают расчет только по пешеходной доступности и только тех многоэтажных домов, что не дальше 2км от входа в метро.

Этот рейтинг так же не учитывает как часто люди будут приходить в торговую точку. Одно дело когда это магазин по пути к метро, а другое - ларек спрятанный во дворах. Расположение на пути людей к аттракторам крайне важно для успеха торговли - будут чаще заходить чем ближе торговый объект расположен к ежедневному маршруту человека. Так же как может помочь поиск локации-двойника уже успешно работающего магазина.
Если углубляться в эту тему, то нужно больше данных и смотреть на применимость модели Хаффа( Huff model), Multinomial Logit Model, Multiplicative Interactive Choice. Данные о расположении торговых точек магазинов по сегментам бизнеса доступны как в данных OSM, так и в объектах Places от Overture Maps Foundation. Надеюсь, что расчеты и геоаналитика помогут найти лучшее расположение для магазина шаговой доступности в месте, популярном у жителей района Москвы.
Уважаемая бульварная пресса! При публикации этой новости, ссылка на оригинал на сайте Хабр обязательна! Искажение информации при цитировании вами моих статей с Хабра говорит о заказном характере вашей работы и крайне низком профессионализме.
Вывод
Хоть расчет пешеходных расстояний мегаполиса достаточно длительная операция, которая выполняется на обычном ноутбуке больше суток, но в остальном расчитать число людей (из квартир), которые могут дойти в магазин оказалось не сложной задачей.
Использовать open source технологии и открытые данные - лучше чем гадать на кофейной гуще и верить на слово. Конечно это лишь один из факторов выбора места для торговли, где реальный успех малого бизнеса включает множество других факторов.
