Задача 009 — веб-исследование: модель данных CRM недвижимости и бесплатная гео-карта
Все страницы просмотрены 2026-11-02. Пометка 🔮 Додумал:
— вывод без прямого подтверждения источником. Пометка «✅ Проверил
запросом» — один тестовый HTTP-запрос из терминала 2026-11-02 (не
браузер).
Главное за 30 секунд
- Квартира ≠ объявление. Объявление (listing) —
запись агентства с ценой и агентом; квартира (unit) — физический объект;
одна квартира → много объявлений. RESO так и делает:
Property= объявление MLS, физический объект — отдельный идентификатор UPI. - В Испании есть бесплатный «ключ дома» — referencia catastral. Первые 14 символов — участок / дом, ещё 4 — конкретная квартира. Catastro отдаёт её по адресу и по координатам без ключа (✅ проверил).
- Координаты без ключа есть у трёх сервисов: Nominatim (1 запрос/с, свой User-Agent, кэшировать), ICGC Geocoder (только Каталония, отдаёт GeoJSON), Photon (fair use).
- Границы 73 barris — Open Data BCN, CC BY 4.0.
Готового WGS84-GeoJSON там нет: есть JSON/CSV с WKT-полем
geometria_wgs84(1 МБ) и ZIP-GeoJSON в EPSG:25831 (2,7 МБ). Конвертация в GeoJSON — 20 строк Python без библиотек, итог ≈366 КБ (✅ проверил). - ⚠️ file:// и тайлы OSM. С 2026 года
tile.openstreetmap.orgотдаёт 403 «Referer is required» страницам, открытым двойным кликом. Выход: открыть черезpython3 -m http.server(localhost) или взять тайлы ICGC. - ⚠️ CARTO больше не бесплатен без ключа: без ключа на тайле водяной знак «API KEY REQUIRED» (✅ проверил) — нам не подходит.
1. Модель данных CRM недвижимости
1.1. Физический объект и объявление — разные сущности
- RESO Data Dictionary 2.0 (стандарт MLS США): ресурс
Propertyописан как «Fields commonly used in a Multiple Listing Service (MLS) listing» — то есть это объявление, а не здание.- В нём есть поля объявления:
ListingKey,ListingId,ListPrice,OriginalListPrice,PreviousListPrice,ClosePrice,StandardStatus,ListAgentKey,ListOfficeKey,BuyerAgentKey,CloseDate,DaysOnMarket. - И поля физического объекта:
UnparsedAddress,Latitude,Longitude,ParcelNumber,BuildingName,UnitNumber,SubdivisionName,MLSAreaMajor. ParcelNumber: «A number used to uniquely identify a parcel or lot. This number is typically issued by the county or county assessor.»- Источник: RESO DD 2.0 Property.
- В нём есть поля объявления:
- RESO UPI (Universal Property Identifier): отдельный
стандарт для физического объекта — URN из страны,
региона, номера участка, типа и подтипа объекта.
- RESO прямо называет назначение: однозначная ссылка на объект, в том числе для дедупликации.
- Источник: upi.reso.org.
- Вывод из RESO: один физический объект (UPI) → много
записей-объявлений (
ListingKey), каждое со своим агентом (ListAgentKey), офисом (ListOfficeKey) и ценой. - idealista: по публикации 2021 года запустил
инструмент, показывающий один объект, выставленный несколькими
агентствами и частными лицами на idealista и других порталах.
- Источник: onlinemarketplaces.com, 2021.
- Критерии склейки idealista не публикует — официального описания не нашёл.
- Испанский аналог UPI — referencia catastral (RC), 20
символов:
- 14 первых — участок (finca / parcela), то есть дом;
- 4 следующих — конкретный объект внутри участка, то есть квартира или помещение;
- 2 последних — контрольные.
- Источник: idealista news — referencia catastral.
- ✅ Проверил запросом: Catastro по адресу «CL SANT CARLES 10,
Barcelona» вернул
pc1=2212501,pc2=DF3821C,car=0001,cc1=H,cc2=K— ровно эта структура.
1.2. Остальные сущности — как они называются в стандартах и CRM
- Список ресурсов RESO DD 2.0 (dd.reso.org/DD2.0/about):
Member— агент (участник MLS).Office— агентство (офис).Teams,TeamMembers— команды агентов.Showing,ShowingAppointment,ShowingRequest,ShowingAvailability— просмотры.OpenHouse— день открытых дверей.Contacts,ContactListings,ContactListingNotes— клиенты, их подборки и заметки по объявлениям.SavedSearch— сохранённые критерии поиска.Prospecting— рассылка подборок клиенту.HistoryTransactional— история изменений полей (цена, статус).Media— фото и документы объявления.PropertyRooms,PropertyUnitTypes— комнаты и типы юнитов.- Отдельного ресурса «дом» и «район» в списке нет: район — поля внутри
Property(MLSAreaMajor,SubdivisionName), дом —BuildingName.
- EspoCRM Real Estate (открытое расширение, AGPLv3)
(espocrm.com):
- Добавляет две сущности:
PropertiesиRequests(запрос клиента = критерии поиска). - Request хранит «desired locations, price range, square range».
- Property хранит «address, price, square, number of bedrooms, number of bathrooms, floor, year built».
- CRM показывает совпадения в обе стороны: подходящие объекты для запроса и подходящие запросы для объекта.
- Интерес клиента к объекту → создаётся
Opportunity(сделка); приClosed Wonобъект и запрос автоматически получают статусCompleted.
- Добавляет две сущности:
- Переговоры / предложение цены: в RESO отдельного
ресурса «offer» в списке DD 2.0 нет; есть
TransactionManagement.- 🔮 Додумал: для личной базы переговоры удобнее хранить как сделку (deal) на паре «мы × объявление» с журналом событий: запрос, просмотр, предложение цены, ответ, итог.
1.3. Сводная модель, применимая к нашему случаю
- 🔮 Додумал (сборка из RESO + EspoCRM + Catastro, под личную базу):
area— район:barriиdistricteиз Open Data BCN, ключcodi_barri.building— дом: ключ = 14 символов RC; адрес, координаты, год постройки.unit— квартира: ключ = 20 символов RC, если известна; иначе адрес + этаж + дверь; ссылка наbuilding.listing— объявление: портал, URL, тип сделки (sale/rent/flex-rent), цена, дата, агент, агентство; ссылка наunit; история цены.agent— агент (как RESOMember): телефон WhatsApp, агентство, языки, заметки о стиле.agency— агентство (как RESOOffice).visit— просмотр (как RESOShowing): дата, кто был, впечатление.deal— переговоры по одному объявлению: стадия, итог, причина отказа, журнал.search_criteria— что ищем (как EspoCRMRequest/ RESOSavedSearch): отдельно «купить на Барселонете», «снять весной 2027», «сабаренда».
2. Нормализация адресов и бесплатные координаты без ключей
2.1. Nominatim (OpenStreetMap) — правила использования, цитаты
- Источник: Nominatim Usage Policy, прочитано 2026-11-02.
- Лимит: «No heavy uses (an absolute maximum of 1 request per second).»
- Один поток: «limit your requests to a single thread.»
- Идентификация: «Provide a valid HTTP Referer or User-Agent identifying the application».
- Кэш: «Results must be cached on your side.»
- Атрибуция: «Clearly display attribution as suitable for your medium.»
- Массовое геокодирование: «As a general rule, bulk geocoding of larger amounts of data is not encouraged.»
- Автодополнение запрещено: «Auto-complete search» — в списке запрещённого; «you must not implement such a service on the client side».
- ✅ Проверил запросом (структурный поиск
street=10 Carrer de Sant Carles&city=Barcelona&countrycodes=es&addressdetails=1):- вернул
lat 41.3790092,lon 2.1888912; - в
addressестьquarter: "la Barceloneta"иsuburb: "Ciutat Vella"— то есть barri и districte приходят сразу; - лицензия ответа: «Data © OpenStreetMap contributors, ODbL 1.0».
- вернул
- Для нас десятки адресов — это не «bulk»; скрипт с паузой 1 с и кэшем в JSON укладывается в правила.
2.2. Photon (komoot) — альтернатива на тех же данных OSM
- Источник: photon.komoot.io.
- Условия: «You can use the API for your project, but please be fair - extensive usage will be throttled.»
- Гарантий нет: «We do not guarantee for the availability and usage might be subject of change in the future.»
- Ключ не упомянут; примеры:
https://photon.komoot.io/api/?q=...&limit=1&lang=..., обратный —https://photon.komoot.io/reverse?lon=..&lat=...
2.3. ICGC Geocoder — официальный геокодер Каталонии
- Источник: ICGC Geocoder; документация openicgc.github.io/geocodificador-doc_en.
- Охват: только Каталония — «obtain the coordinates of a place located in Catalonia from its postal address».
- Операции:
cerca(полный поиск),autocompletar, обратное геокодирование; базовый URLhttps://eines.icgc.cat/geocodificador. - ✅ Проверил запросом
https://eines.icgc.cat/geocodificador/cerca?text=Carrer de Sant Carles 10, Barcelona&size=1:- ответ — GeoJSON
FeatureCollection, точка[2.18889116, 41.37900915]; - есть нормализованные поля:
tipus_via,nom_via,portal,codi_postal,municipi,etiqueta.
- ответ — GeoJSON
- Ключ и лимиты на странице не указаны; лицензия ответов не указана.
- 🔮 Додумал: ключ не нужен (запрос прошёл без ключа), но условия использования не опубликованы — держать паузу и кэш как для Nominatim.
2.4. Catastro (Sede Electrónica del Catastro) — открытый поиск по адресу и по референции
- Источник: datos.gob.es
— Servicios de datos catastrales no protegidos.
- Даются «datos no protegidos de Catastro (sin titularidad ni valor catastral)» — без собственника и кадастровой стоимости.
- Охват — вся Испания, кроме Страны Басков и Наварры.
- Сервисы: списки провинций, муниципалитетов, улиц и номеров; данные объекта по адресу, по RC, по polígono-parcela; перевод RC ↔︎ координаты.
- Условия — в PDF licdescargaES.pdf (не открывал).
- OVCCoordenadas:
Consulta_RCCOOR(RC по координатам),Consulta_CPMRC(координаты по RC),Consulta_RCCOOR_Distancia(RC рядом с точкой). - ✅ Проверил запросом
Consulta_RCCOOR?SRS=EPSG:4326&Coordenada_X=2.18889116&Coordenada_Y=41.37900915(HTTP GET, без ключа):- вернул RC участка
2212501DF3821Cи адресCL PESCADORS 13— угловой дом, у которого второй адрес — Sant Carles 10.
- вернул RC участка
- ✅ Проверил запросом JSON-вариант
.../OVCWcfCallejero/COVCCallejero.svc/json/Consulta_DNPLOC?Provincia=BARCELONA&Municipio=BARCELONA&Sigla=CL&Calle=SANT CARLES&Numero=10:- вернул полный RC
2212501DF3821C0001HK, назначение, площадь, год постройки 1936, тип участка «Parcela con varios inmuebles (division horizontal)» и ссылку на кадастровую карту.
- вернул полный RC
- Вывод: Catastro даёт ключ дома (14 символов) и решает «один дом — два адреса» (угловые дома на Барселонете).
2.5. Открытые данные Барселоны — границы barris и districtes
- Страница набора: Administrative units of the city of Barcelona.
- Лицензия: Creative Commons Attribution 4.0 (CC BY 4.0).
- Состав: 10 districtes, 73 barris (коды утверждены 22.12.2006).
- Ресурсы, полезные нам (✅ скачал и проверил формат 2026-11-02):
BarcelonaCiutat_Barris.json— прямая ссылка → редирект наhttps://opendata-ajuntament.barcelona.cat/resources/bcn/EstadisticaUnitatsAdministratives/BarcelonaCiutat_Barris.json.- Размер 1 059 023 байт.
- Формат: не GeoJSON, а JSON-массив из 73 записей с
полями
codi_districte,nom_districte,codi_barri,nom_barri,geometria_etrs89,geometria_wgs84. - Геометрия — текст WKT (
POLYGON×71,MULTIPOLYGON×2), координаты WGS84 (lon, lat) — сразу годятся для Leaflet после перевода в GeoJSON.
BarcelonaCiutat_Barris.csv— прямая ссылка, 1 051 160 байт, те же поля.BarcelonaCiutat_Districtes.json— прямая ссылка.Unitats_Administratives_BCN.geojson— прямая ссылка.- На деле ZIP 2 738 138 байт с двумя файлами:
..._POLIGONS.json(22,8 МБ) и..._PUNTS.json. - Внутри все уровни сразу (1501 полигон: TERME, DISTRICTE, BARRI, AEB,
SEC_CENS и др.), фильтр по
TIPUS_UA == "BARRI". - Проекция EPSG:25831 (UTM 31N, метры) — Leaflet без перепроекции не покажет. Нам неудобен.
- На деле ZIP 2 738 138 байт с двумя файлами:
- ✅ Проверил: конвертация
geometria_wgs84→ GeoJSONMultiPolygonна чистом Python (regex, без библиотек) даёт файл ≈366 КБ при 6 знаках после запятой. - ✅ Проверил: точка Sant Carles 10 по лучевому алгоритму попадает
ровно в
la Barceloneta. - Сторонняя готовая версия: martgnz/bcn-geodata —
GeoJSON/TopoJSON, данные на 2020 год,
barris/barris.json— TopoJSON (нуженtopojson-client). Брать официальный источник надёжнее.
2.6. Как привязать точку к району
- Turf.js
booleanPointInPolygon(документация, v7.4.0):- «Takes a Point and a Polygon or MultiPolygon and determines if the point resides inside the polygon.»
- Вызов:
turf.booleanPointInPolygon(turf.point([lon, lat]), feature); опцияignoreBoundary. - Порядок координат в GeoJSON —
[lon, lat], а в LeafletL.latLng(lat, lon)— частая ошибка.
- Без библиотек: лучевой алгоритм (ray casting) — 8 строк; для 73 полигонов и десятков точек скорости хватает (✅ проверил на Python).
- Ещё проще — взять район из ответа геокодера:
Nominatim отдаёт
quarter(barri) иsuburb(districte).- 🔮 Додумал: названия OSM и Open Data BCN могут расходиться в мелочах
(артикль, регистр) — сверять по коду
codi_barriпосле point-in-polygon, а не по строке.
- 🔮 Додумал: названия OSM и Open Data BCN могут расходиться в мелочах
(артикль, регистр) — сверять по коду
- 🔮 Додумал: считать район один раз при добавлении объекта и хранить
codi_barriв записи — тогда карта и фильтры не зависят от Turf в браузере.
3. Карта локально без сервера
3.1. Leaflet — версия и подключение
- Стабильная версия — Leaflet 1.9.4 (релиз
18.05.2023); 2.0.0-alpha.1 — предварительная (leafletjs.com/download);
npm
latest= 1.9.4. - Официальные теги с
integrity— в блоке «Готовые ссылки для прототипа» ниже. - Встроенный GeoJSON: данные кладутся в
<script>const LISTINGS = {...}</script>прямо в HTML.- 🔮 Додумал:
fetch('listings.geojson')со страницыfile://браузеры (особенно Chrome) блокируют; встраивание данных в HTML этого избегает.
- 🔮 Додумал:
3.2. Фильтр по району и типу сделки
L.geoJSON(data, { filter })— «Thefilteroption can be used to control the visibility of GeoJSON features» (пример Leaflet).- Функция получает
featureи возвращаетtrue/false.
- Функция получает
onEachFeature— вызывается на каждый объект до добавления; там вешаетсяbindPopupс ценой, ссылкой, агентом.pointToLayer— заменяет стандартный маркер, например наL.circleMarkerс цветом по типу сделки.- Слои и переключатель: по слою
L.layerGroupна каждый тип сделки (sale,rent,flex-rent,sublet) +L.control.layers(null, overlays)— галочки в углу карты без своего кода. - Фильтр по району: 🔮 Додумал — выпадающий список
<select>изnom_barri; при смене —layer.clearLayers()иaddData()с новымfilter; полигоны barris — отдельным слоем с подписями.
3.3. Группировка близких точек — Leaflet.markercluster
- Источник: Leaflet/Leaflet.markercluster; требует Leaflet ≥ 1.0.0.
- Последняя версия на npm и cdnjs — 1.5.3 (опубликована 18.10.2021); README местами ещё ссылается на 1.4.1.
- Пример:
var markers = L.markerClusterGroup(); markers.addLayer(L.marker(...)); map.addLayer(markers);. - Опции:
maxClusterRadius(по умолчанию 80 px),disableClusteringAtZoom(на этом зуме и ближе — без кластеров). - 🔮 Додумал: на Барселонете дома стоят плотно, и несколько объявлений
одного дома дают одну точку — нужен
disableClusteringAtZoom: 18илиspiderfyOnMaxZoom(по умолчанию включён), чтобы точки раскрывались.
3.4. Тайлы и file:// — что работает, а что нет
- OSM Tile Usage Policy (operations.osmfoundation.org/policies/tiles):
- URL — только
«
https://tile.openstreetmap.org/{z}/{x}/{y}.png»; «Other subdomains or hostnames may be slower or withdrawn without notice.» - Referer: «Browsers are expected to send a valid
Refererheader.» - Блокировки: «Traffic using generic defaults, referer-stripping, or spoofed identities may be blocked without notice.»
- Атрибуция: «Show OpenStreetMap licence attribution clearly on the map (typically bottom-right).»
- Офлайн: «Offline use is not permitted on
tile.openstreetmap.org.» - Массовая выкачка запрещена: «Bulk downloading is any pre-emptive fetching of tiles other than those a user is actively viewing.»
- Числового порога «тяжёлого» использования политика не даёт;
file://не упоминает.
- URL — только
«
- Что происходит с file:// на практике (форум
OSM, март 2026):
- Страница Leaflet, открытая с диска, получает 403 с текстом «Referer is required by tile usage policy of to OpenStreetMap's volunteer-run servers».
- С
file://браузер Referer не шлёт, а подставить его вручную браузер не даёт. - Блокировка идёт «brownout»-ом — часть тайлов грузится, часть нет.
- Работающий обход из треда: открыть ту же страницу через локальный
сервер
python -m http.server --bind 127.0.0.1. <meta name="referrer">помог на веб-сервере, но не с одиночного локального файла.
- OSM wiki «Blocked» (wiki.openstreetmap.org/Blocked,
правка 10.08.2026): для сайтов — выставить разрешающий
Referrer-Policy; у Leaflet для слоя есть опцияreferrerPolicy; блок снимается сам после исправления. - ✅ Проверил запросом (curl, не браузер): тайл OSM с собственным
User-Agent отдаётся 200 и без Referer, и с
Referer: http://localhost:8000/.- Это не доказывает работу из браузера через
file://— там браузерный User-Agent и пустой Referer, что форум описывает как блокируемый случай.
- Это не доказывает работу из браузера через
- CARTO basemaps — больше не вариант без ключа (условия от 29.09.2026,
страница ключа):
- бесплатный уровень — только «any Customer who requests and uses a CARTO-issued API key»;
- запросы без ключа «may receive a visible watermark»;
- некоммерческий лимит — 5 000 000 тайлов в месяц, но с ключом и регистрацией;
- ✅ Проверил запросом: тайл
a.basemaps.cartocdn.com/rastertiles/voyager/...без ключа — картинка с надписью «API KEY REQUIRED».
- ICGC Base Map Service — бесплатные тайлы без ключа
(icgc.cat
Base Map Service):
- XYZ в EPSG:3857:
https://geoserveis.icgc.cat/servei/catalunya/mapa-base/wmts/topografic/MON3857NW/{z}/{x}/{y}.png, зумы 0–20. - Лицензия: «Catalonia ©ICGC. License CC-BY ( attribution ) / Rest of the world ©OpenMapTiles ©OpenStreetMap contributors. ODbL license».
- Ключ и регистрация на странице не упомянуты.
- ✅ Проверил запросом: тайл зума 16 над Барселонетой отдаётся 200 без
Referer (формат фактически
image/jpeg). - 🔮 Додумал: раз сервер отвечает без Referer, страница с
file://должна грузить эти тайлы; в браузере не проверял.
- XYZ в EPSG:3857:
3.5. Готовые онлайн-инструменты (для сравнения)
- uMap (umap.openstreetmap.fr): карта поверх OSM с
импортом CSV и GeoJSON; анонимные карты с секретной ссылкой
редактирования описаны ещё в релизе 0.3.0 (OSM
dev, 2013).
- Сайт uMap отдал 403 нашему загрузчику; текущие настройки видимости не проверил.
- Минус для нас: данные уходят на чужой сервер; карта по ссылке может стать доступной посторонним.
- geojson.io (github.com/mapbox/geojson.io):
«A fast, simple editor for map data», лицензия ISC, импорт и экспорт
GeoJSON, KML, CSV, Shapefile.
- Работает на Mapbox GL JS; для своей копии нужен Mapbox-токен — нам не подходит как основа.
- Годится как разовый инструмент: поправить точку или нарисовать контур и сохранить GeoJSON.
4. История переговоров и «предпочтения по переговорам» в CRM
4.1. Стадии воронки (pipeline stages)
- HubSpot, воронка по умолчанию (knowledge.hubspot.com):
- Appointment scheduled (20%) → Qualified to buy (40%) → Presentation scheduled (60%) → Decision maker bought-in (80%) → Contract sent (90%) → Closed won (100%) / Closed lost (0%).
- Обязательны обе закрытые стадии — Won и Lost, иначе отчёты считают неверно.
- Можно сделать поле обязательным при переходе на стадию: «If a property is required, users cannot create or update the record until they set a value for it.»
- Follow Up Boss (CRM для риелторов) (help.followupboss.com): стадии контакта — Hot Prospect, Nurture, Active Client, Closed, Past Client; Lead, Closed и Trash — системные (по стороннему разбору).
- 🔮 Додумал — стадии для нашей личной базы:
- Покупка:
found→asked-agent→visit-scheduled→visited→offer-made→negotiating→reserved(arras) →notary→won/lost. - Аренда (в т.ч. Ukio):
found→asked-availability→visit-or-video→applied→documents-sent→contract-signed→won/lost. - Сабаренда (Константин — арендатор и арендодатель):
отдельная воронка «жилец»:
inquiry→viewing→agreed→moved-in→moved-out.
- Покупка:
4.2. Итог и причина отказа (won / lost, lost reason)
- Pipedrive (support.pipedrive.com
— Lost reasons):
- По умолчанию причина — свободный текст: «By default, Pipedrive is set to use freeform lost reasons.»
- Можно завести готовый список до 100 причин — данные тогда сравнимы.
- Комментарий к причине сохраняется заметкой в сделке.
- Рекомендация: смотреть причины в отчётах, чтобы видеть повторяющиеся.
- HubSpot (стандартные
поля сделки): поле
Closed lost reason— причина потери сделки; сообщество советует сделать его обязательным на стадии Closed lost. - 🔮 Додумал — короткий список причин для нас:
price-too-high,sold-or-rented-to-other,bad-condition,noise-or-street,floor-or-no-lift,agent-unresponsive,owner-refused-offer,we-chose-other,documents-problem, плюс свободный комментарий.
4.3. Заметки об агенте и стиле общения
- obsidian-crm (github.com/TalkingQuickly/obsidian-crm):
- Человек — отдельная заметка с frontmatter:
name,company,email,phone,tags,created,last_contact. - Каждое взаимодействие — отдельная заметка
YYYY-MM-DD - {{person}} - {{title}}со ссылкой на человека; разделы: краткое содержание, чек-лист действий, заметки. last_contactобновляется автоматически при добавлении заметки; Dataview строит таблицы «кому давно не писали».
- Человек — отдельная заметка с frontmatter:
- Форум Obsidian: Dataview надёжно читает YAML-frontmatter, а поля в теле заметки — не всегда (тред о frontmatter vs тело).
- 🔮 Додумал — поля «предпочтений по переговорам» агента:
preferred_channel(whatsapp-text / voice / call),languages,response_speed,negotiable(да / нет / по цене / по срокам),commission_policy,works_for(продавец / покупатель / оба),reliability_notes,last_contact.- Заметки — атомарными пунктами с датой и ссылкой на сообщение WhatsApp, чтобы факт можно было проверить.
4.4. Что из этого применимо к маленькой личной базе markdown + JSON
- Frontmatter + отдельные заметки-события (как obsidian-crm) — да: читается человеком, ищется grep-ом, Dataview не обязателен.
- Стадии с вероятностями (HubSpot) — нет: вероятность для двух людей не нужна, достаточно стадии и даты.
- Готовый список причин отказа (Pipedrive) — да: через полгода видно, почему отваливались варианты.
- Обязательное поле на стадии (HubSpot) — да в виде правила
валидатора:
lostбезlost_reasonне сохраняется. - Matching запрос ↔︎ объект (EspoCRM) — частично: фильтр карты по
критериям из
search_criteriaзакрывает потребность.
Что берём для маленькой личной базы
- Разделить
unitиlisting. Довод: одну квартиру на Барселонете выставляют 2–3 агентства по разной цене — так делает RESO (Property = объявление, UPI = объект) и idealista при склейке дублей; иначе сравнение цен невозможно. - Ключ дома — 14 символов referencia catastral, ключ квартиры — 20. Довод: бесплатно, без ключа API, однозначно решает «угловой дом с двумя адресами» (проверено на Sant Carles 10 / Pescadors 13).
- Координаты — ICGC Geocoder первым, Nominatim запасным, с кэшем в JSON. Довод: ICGC официальный для Каталонии и отдаёт нормализованную улицу и номер; Nominatim требует 1 запрос/с, свой User-Agent и кэш — для десятков адресов это одна минута.
- Район считать один раз и хранить
codi_barriв записи. Довод: карта и фильтры работают без Turf и без сети; источник — официальный JSON Open Data BCN (CC BY 4.0), перевод в GeoJSON ≈366 КБ одним скриптом. - Карта — один HTML с Leaflet 1.9.4 с CDN и встроенным
GeoJSON. Довод: ни сервера, ни сборки; фильтр через
L.geoJSON({filter})иL.control.layers, кластеры — markercluster 1.5.3. - Тайлы — ICGC topografic по умолчанию, OSM — только через
python3 -m http.server. Довод: OSM с 2026 года блокирует страницы сfile://без Referer; CARTO теперь требует ключ; ICGC отвечает без Referer и без ключа, лицензия CC-BY. - Переговоры — сущность
dealна пару «мы × объявление» с журналом событий. Довод: так устроены Pipedrive и HubSpot; история цены, просмотров и ответов агента — в одном месте, а не размазана по переписке. - Короткий фиксированный список
lost_reason+ свободный комментарий, обязательный приlost. Довод: Pipedrive и HubSpot советуют готовый список ради сравнимости; валидатор не даст закрыть сделку без причины. - Агент — markdown-заметка с frontmatter, взаимодействия — датированные пункты со ссылкой на сообщение WhatsApp. Довод: паттерн obsidian-crm проверен на личных CRM, читается без программ, а ссылка на сообщение делает каждый факт проверяемым.
- Отдельные воронки для покупки, аренды и сабаренды, а не одна общая. Довод: этапы разные (arras и нотариус против документов и договора аренды); общий список стадий путает фильтры.
Готовые ссылки для прототипа
Leaflet 1.9.4 (официальные теги с leafletjs.com/download)
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>- То же с cdnjs (SRI из api.cdnjs.com):
- CSS
https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css—sha512-Zcn6bjR/8RZbLEpLIeOwNtzREBAJnUKESxces60Mpoj+2okopSAcSUIUOseddDm0cxnGQzxIR7vJgsLZbdLE3w== - JS
https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js—sha512-BwHfrr4c9kmRkLw6iXFdzcdWV/PGkVgiIyIWLLlTSXzWQzxuSg4DiQUCpauz/EWjgk5TYQqX/kvn9pG1NpYfqg==
- CSS
Leaflet.markercluster 1.5.3 (cdnjs, SRI из api.cdnjs.com)
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.5.3/MarkerCluster.css" integrity="sha512-mQ77VzAakzdpWdgfL/lM1ksNy89uFgibRQANsNneSTMD/bj0Y/8+94XMwYhnbzx8eki2hrbPpDm0vD0CiT2lcg==" crossorigin="anonymous" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.5.3/MarkerCluster.Default.css" integrity="sha512-6ZCLMiYwTeli2rVh3XAPxy3YoR5fVxGdH/pz+KMCzRY2M65Emgkw00Yqmhh8qLGeYQ3LbVZGdmOX9KUjSKr0TA==" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.5.3/leaflet.markercluster.js" integrity="sha512-OFs3W4DIZ5ZkrDhBFtsCP6JXtMEDGmhl0QPlmWYBJay40TT1n3gt2Xuw8Pf/iezgW9CdabjkNChRqozl/YADmg==" crossorigin="anonymous"></script>Turf.js 7.4.0 (только если point-in-polygon нужен в браузере)
https://cdnjs.cloudflare.com/ajax/libs/Turf.js/7.4.0/turf.min.js—sha512-822zqm9Mz7MyVmwNEtqsJeorg2f0jyeNAj/v22L/HpNPbnmh4Ol+JCfmG/IX8s4iR36sELngHBSWzK7NHT9NNg==
Тайлы с атрибуцией
- ICGC topografic — по умолчанию, работает без Referer (curl-проверка):
L.tileLayer('https://geoserveis.icgc.cat/servei/catalunya/mapa-base/wmts/topografic/MON3857NW/{z}/{x}/{y}.png', {
maxZoom: 20,
attribution: 'Catalonia © <a href="https://www.icgc.cat/">ICGC</a> (CC BY) | Rest of world © OpenMapTiles © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
}).addTo(map);- OSM — только если страница открыта через
http://localhost(python3 -m http.server --bind 127.0.0.1):
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
referrerPolicy: 'strict-origin-when-cross-origin',
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
}).addTo(map);- CARTO — не использовать: с 2026 года без ключа отдаёт водяной знак.
Границы районов Барселоны (Open Data BCN, CC BY 4.0, атрибуция «© Ajuntament de Barcelona»)
- Barris, JSON с полем WKT
geometria_wgs84(1,06 МБ, 73 записи):https://opendata-ajuntament.barcelona.cat/data/dataset/808daafa-d9ce-48c0-925a-fa5afdb1ed41/resource/75197dfe-0306-4c5e-9643-34948af07fb6/download - Districtes, JSON с WKT:
https://opendata-ajuntament.barcelona.cat/data/dataset/808daafa-d9ce-48c0-925a-fa5afdb1ed41/resource/5f8974a7-7937-4b50-acbc-89204d570df9/download - Настоящий GeoJSON (ZIP, все уровни, EPSG:25831 — нужна
перепроекция):
https://opendata-ajuntament.barcelona.cat/data/dataset/808daafa-d9ce-48c0-925a-fa5afdb1ed41/resource/cd800462-f326-429f-a67a-c69b7fc4c50a/download - Barceloneta в наборе:
codi_districte "01"(Ciutat Vella),nom_barri "la Barceloneta".
Геокодеры (без ключей, с кэшем, не чаще 1 запроса в секунду)
- ICGC:
https://eines.icgc.cat/geocodificador/cerca?text=<адрес>, Barcelona&size=1 - Nominatim:
https://nominatim.openstreetmap.org/search?street=<номер> <улица>&city=Barcelona&countrycodes=es&format=jsonv2&addressdetails=1&limit=1+ собственныйUser-Agent - Catastro, RC по адресу (JSON):
https://ovc.catastro.meh.es/OVCServWeb/OVCWcfCallejero/COVCCallejero.svc/json/Consulta_DNPLOC?Provincia=BARCELONA&Municipio=BARCELONA&Sigla=CL&Calle=<УЛИЦА>&Numero=<N> - Catastro, RC по координатам (XML):
https://ovc.catastro.meh.es/ovcservweb/ovcswlocalizacionrc/ovccoordenadas.asmx/Consulta_RCCOOR?SRS=EPSG:4326&Coordenada_X=<lon>&Coordenada_Y=<lat>