Metadata-Version: 2.4
Name: pynspd
Version: 0.4.5
Summary: Python-библиотека для работы с https://nspd.gov.ru (ex-ПКК)
Project-URL: Repository, https://github.com/yazmolod/pynspd
Author-email: Aleksandr Litovchenko <yazmolod@gmail.com>
License: MIT License
        
        Copyright (c) 2025 Aleksandr Litovchenko
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Requires-Dist: geojson-pydantic>=1.2.0
Requires-Dist: httpx>=0.28.1
Requires-Dist: mercantile>=1.2.1
Requires-Dist: numpy>1.24.4
Requires-Dist: pydantic>=2.10.4
Requires-Dist: pyproj>=3.7
Requires-Dist: shapely>=2.0.6
Description-Content-Type: text/markdown

# PyNSPD

<p align="center">
  <em> Python-библиотека для работы с <a href="https://nspd.gov.ru" target="_blank">НСПД - Национальной системой пространственных данных</a> (ex-ПКК)</em>
</p>
<p align="center">
  <a href="https://pypi.org/project/pynspd/" target="_blank">
      <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/pynspd">
  </a>
</p>

---

> ⚠️ **Важно**
> - Это неофициальная библиотека
> - НСПД часто меняет схемы объектов. Если у вас происходит ошибка валидации - попробуйте обновить библиотеку

## Особенности
- **Синхронное и асинхронное API**: полностью идентичные API позволяют одинаково удобно работать как в старых синхронных, так и высокопроизводительных асинхронных проектах.
- **Полная типизации на [Pydantic](https://github.com/pydantic/pydantic)**: удобная работа с ответами благодаря автозаполнениям от IDE.
- **Автогенерация типов**: данные о перечне слоев, их полях и их типов подтягиваются напрямую с НСПД.
- **Встроенная поддержка [shapely](https://github.com/shapely/shapely)**: удобная аналитика полученной геометрии.


## Пример использования
### API поиска (на примере ЗУ)

**По кадастровому номеру**:

```python
from pynspd import AsyncNspd

async with AsyncNspd() as nspd:
    feat = await nspd.search_zu("77:05:0001005:19")

    # исходная геометрия - geojson
    print(feat.geometry.model_dump())
    # {'type': 'Polygon', 'coordinates': ...}
    
    # но можем легко конвертировать в shapely
    print(type(feat.geometry.to_shape()))
    # <class 'shapely.geometry.polygon.Polygon'>

    # или принудительно привести к мульти-типу
    print(type(feat.geometry.to_multi_shape()))
    # <class 'shapely.geometry.multipolygon.MultiPolygon'>

    # Доступ ко всему переченю свойств объекта
    print(feat.properties.options.model_dump())
    # {'land_record_type': 'Земельный участок', ...}

    # А также форматирование свойств по примеру карточки с сайта
    print(feat.properties.options.model_dump_human_readable())
    # {'Вид объекта недвижимости': 'Земельный участок', ...}
```

**В точке**:
```python
from shapely import Point

features = await nspd.search_zu_at_point(Point(37.546440653, 55.787139958))
print(features[0].properties.options.cad_num)
# "77:09:0005008:11446"
```

**В контуре**:
```python
from shapely import wkt

contour = wkt.loads(
    "Polygon ((37.62381 55.75345, 37.62577 55.75390, 37.62448 55.75278, 37.62381 55.75345))"
)
features = await nspd.search_zu_in_contour(contour)
cns = [i.properties.options.cad_num for i in features]
print(cns)
> ["77:01:0001011:8", "77:01:0001011:14", "77:01:0001011:16"]
```

### Типизированный поиск объекта из любого слоя
Шорткаты для типизированного поиска из примеров выше реализованы только для часто используемых слоев *"Земельные участки ЕГРН"* и *"Здания"*. Однако, мы можем искать в любых слоях, представленных на НСПД
```python
# либо импортируем определение слоя, зная его id (с сайта)
from pynspd.schemas import Layer37578Feature as lf_def
# либо найти определение слоя по названию, 
# но тогда объект будет типизирован частично
from pynspd import NspdFeature
lf_def = NspdFeature.by_title(
    "ЗОУИТ объектов энергетики, связи, транспорта" # IDE знает весь перечень слоев 
)                                                  # и подсказывает ввод
feat = await nspd.search_by_model("Останкинская телебашня", lf_def) 

# аналогично для остального API
# nspd.search_in_contour_by_model(contour, lf_def)
# nspd.search_at_point_by_model(pt, lf_def)
```

### Поиск объекта из неизвестного слоя
```python
feat = await nspd.search_by_theme("77:01:0004042:23609")
print(feat.properties.category_name)
# 'Объекты незавершенного строительства'
```

В данном случае вернется нетипизированный объект `NspdFeature`, 
в котором все еще доступны геометрия и все свойства, но уже без подсказок IDE и метода `.model_dump_human_readable()`

Чтобы это исправить, можно воспользоваться методом `.cast()`

```python
# Исходный результат поиска
print(type(feat).__name__)
# NspdFeature
print(feat.layer_meta.layer_id, feat.layer_meta.category_id)
# raise AttributeError

# Автоопределение типа
# Быстрый способ, но объект останется без подсказок IDE 
# и возможна ошибка UnknownLayer
casted_feat = feat.cast()
print(type(casted_feat).__name__)
# Layer36329Feature

# Ручное определение типа
# Будут активны подсказки, 
# но возможна ошибка валидации, если модель не соответствует объекту
from pynspd.schemas import Layer36329Feature 
casted_feat = feat.cast(Layer36329Feature)
print(casted_feat.layer_meta.category_id)
# 36384

# Также мы можем привести к типу только свойства
props = feat.properties
print(props.options.model_dump_human_readable())
# {}

# Автоопределение без подсказок IDE
print(props.cast().options.human_readable())
# {'Кадастровый номер': '77:01:0004042:23609', ...}

# Ручное определение
from pynspd.schemas import Options36384 
print(props.cast(Options36384).options.human_readable())
# {'Кадастровый номер': '77:01:0004042:23609', ...}
```


## Установка
```
pip install pynspd
```

## Зависимости
- `httpx` - запросы к API НСПД
- `pydantic`, `geojson-pydantic` - типизации проекта
- `pyproj`, `shapely` - для конвертации geojson-геометрии в удобный для аналитики формат
-  `mercantile` - решение задач обратного геокодирования