5. Parking Zones
Раздел описывает парковочные зоны, их геометрию на карте, геометрию в изображении камеры и текущую оценку занятости.
5.1 Совместимость с текущей реализацией
Раздел сохраняет существующие основные эндпоинты:
GET /zonesPOST /zones/newGET /zones/<zone_id>PUT /zones/<zone_id>DELETE /zones/<zone_id>
Для сохранения совместимости:
- существующие пути эндпоинтов сохранены;
- сохранены ключевые поля бизнес-модели:
zone_id,camera_id,zone_type,capacity,occupied,confidence,pay,occupancy_updated_at; - вместо старого поля
pointsиспользуются:geometry— геометрия зоны на карте;image_polygon— геометрия зоны в пиксельных координатах изображения камеры.
В новом контракте поле
pointsне используется.
5.2 Общие правила
Авторизация
Все эндпоинты раздела требуют авторизации.
Authorization: Bearer <access_token>
Модель доступа
Для эндпоинтов раздела используются следующие разрешения:
zones.view— просмотр зон;zones.create— создание зон;zones.update— изменение зон;zones.delete— удаление зон.
Правила видимости данных
Сервер возвращает только те зоны, которые доступны текущему пользователю в рамках его глобальных прав, членства в партнёрской организации и области доступа к данным.
Для зон партнёров необходимо использовать:
partner_id— владелец зоны;created_by_user_id— пользователь, создавший зону.
Это позволяет применять read_scope, write_scope и delete_scope к зонам партнёра.
Общие ошибки
| Код | Тип | Описание |
|---|---|---|
| 400 | Bad Request | Невалидный JSON или неверные типы полей. |
| 401 | Unauthorized | Токен отсутствует, невалиден, истёк или сессия завершена. |
| 403 | Forbidden | У пользователя недостаточно прав для выполнения действия. |
| 404 | Not Found | Зона или связанная камера не найдена. |
| 409 | Conflict | Конфликт данных. |
| 415 | Unsupported Media Type | Неверный Content-Type. Используй application/json. |
| 422 | Unprocessable Entity | Ошибка валидации. |
| 500 | Internal Server Error | Необработанная ошибка сервера. |
| 503 | Service Unavailable | Сервис временно недоступен. |
Пример ответа:
{
"error_description": "Validation error: geometry must be a valid polygon"
}
5.3 Формат данных
Время
Все временные значения передаются в формате UTC ISO 8601:
2026-04-06T10:00:00Z
Геометрия зоны на карте
Поле geometry задаётся в формате GeoJSON Polygon.
Правила:
- используется объект вида
{ "type": "Polygon", "coordinates": [...] }; - координаты задаются в формате
[longitude, latitude]; - внешний контур должен быть замкнут;
- первая и последняя координаты внешнего контура должны совпадать.
Геометрия зоны в изображении камеры
Поле image_polygon задаёт контур зоны в координатах изображения камеры.
Правила:
- это массив из 4 вершин;
- каждая вершина задаётся как
[x, y]; - вершины должны идти по часовой стрелке;
- значения
xиyдолжны быть целыми числами>= 0.
5.4 Модель Zone
Объект зоны в ответах API.
Базовые поля
zone_id(integer) — уникальный идентификатор зоны.camera_id(integer) — ID камеры, к которой относится зона.zone_type(string, enum:parallel|standard) — тип парковки по способу постановки.capacity(integer,>= 0) — вместимость зоны.occupied(integer,>= 0,<= capacity) — текущее число занятых мест.free_count(integer,>= 0) — текущее число свободных мест.confidence(float,0..1) — достоверность оценкиoccupied.pay(integer,>= 0) — стоимость в рублях в час.geometry(GeoJSON Polygon) — геометрия зоны на карте.image_polygon(array[4]) — геометрия зоны в пиксельных координатах изображения камеры.occupancy_updated_at(string, ISO 8601) — время последнего обновления оценки занятости.created_at(string, ISO 8601) — дата создания.updated_at(string, ISO 8601) — дата последнего обновления.
Дополнительные поля
partner_id(integer | null) — идентификатор партнёра-владельца зоны.created_by_user_id(integer | null) — идентификатор пользователя, создавшего зону.is_active(boolean) — активна ли зона.location_type(string | null) — тип расположения зоны. Используемые значения:street(Уличная парковка вдоль дороги или на проезжей части. У тротуара, в карманах, выделенная синим и т.д.)yard(Парковка во дворе жилого дома, на территории жилого комплекса или офисного здания)open_lot(Открытая наземная парковочная площадка. Если это отдельная площадка, а не уличные места вдоль дороги)underground(Подземная парковка или подземный паркинг.)multilevel(Наземный крытый паркинг на несколько уровней)
is_private(boolean | null) — является ли частной или ограниченно доступной парковкой. Например, только для сотрудников компании или жителей дома.is_accessible(boolean | null) — относится ли зона к парковке только для инвалидов.confidence_level(string | null) — категориальная оценка достоверности:very_lowlowmediumhigh
Поля
partner_id,created_by_user_id,is_active,location_type,is_private,is_accessible,confidence_levelявляются дополнительными.
Пример
{
"zone_id": 1,
"camera_id": 1,
"zone_type": "parallel",
"capacity": 7,
"occupied": 5,
"free_count": 2,
"confidence": 0.76,
"confidence_level": "medium",
"pay": 0,
"geometry": {
"type": "Polygon",
"coordinates": [
[
[30.309426, 59.955976],
[30.309358, 59.956008],
[30.309979, 59.956231],
[30.310043, 59.956201],
[30.309426, 59.955976]
]
]
},
"image_polygon": [
[45, 23],
[87, 25],
[79, 149],
[32, 145]
],
"partner_id": 10,
"created_by_user_id": 123,
"is_active": true,
"location_type": "street",
"is_private": false,
"is_accessible": false,
"occupancy_updated_at": "2026-04-06T10:10:00Z",
"created_at": "2026-04-06T10:00:00Z",
"updated_at": "2026-04-06T10:10:00Z"
}
5.5 Модель ZoneMapItem
Облегчённая модель зоны для отображения на карте.
zone_id(integer) — уникальный идентификатор зоны.zone_type(string) — тип парковки.capacity(integer) — вместимость зоны.occupied(integer) — число занятых мест.free_count(integer) — число свободных мест.confidence(float) — достоверность оценки занятости.confidence_level(string | null) — категориальная оценка достоверности.pay(integer) — стоимость парковки в рублях в час.geometry(GeoJSON Polygon) — геометрия зоны на карте.location_type(string | null) — тип расположения.is_private(boolean | null) — является ли частной или ограниченно доступной парковкой. Например, только для сотрудников компании или жителей дома.is_accessible(boolean | null) — признак специальной парковки.occupancy_updated_at(string, ISO 8601) — время обновления занятости.is_active(boolean) — активна ли зона.
Пример
{
"zone_id": 1,
"zone_type": "parallel",
"capacity": 7,
"occupied": 5,
"free_count": 2,
"confidence": 0.76,
"confidence_level": "medium",
"pay": 0,
"geometry": {
"type": "Polygon",
"coordinates": [
[
[30.309426, 59.955976],
[30.309358, 59.956008],
[30.309979, 59.956231],
[30.310043, 59.956201],
[30.309426, 59.955976]
]
]
},
"location_type": "street",
"is_private": false,
"is_accessible": false,
"occupancy_updated_at": "2026-04-06T10:10:00Z",
"is_active": true
}
5.6 GET /zones
Возвращает список парковочных зон, доступных текущему пользователю.
Требуемые разрешения
zones.view
Query-параметры (необязательные)
camera_id(integer) — вернуть зоны только указанной камеры.partner_id(integer) — вернуть зоны только указанного партнёра.is_active(boolean) — фильтр по активности зоны.min_free_count(integer) — фильтр по минимальному числу свободных мест.min_confidence(float,0..1) — фильтр по достоверности оценки занятости.max_pay(integer) — фильтр по максимальной стоимости парковки в час.include_private(boolean) — возвращать ли частные или ограниченно доступные парковки.include_accessible(boolean) — возвращать ли инвалидные парковки.hide_location_types(string) — перечисление типов расположения для исключения из ответа, напримерstreet,yard.bbox(string) — пространственный фильтр в формате<min_longitude>,<min_latitude>,<max_longitude>,<max_latitude>.view(string) — режим ответа. Поддерживаемые значения:full— полная модель зоны (по умолчанию);map— облегчённая модель для карты.
Пример bbox
30.30,59.92,30.35,59.97
Параметр
bboxрекомендуется использовать при загрузке зон для карты, чтобы не запрашивать все зоны сразу.
Правило применения булевых и категориальных фильтров:
Если у зоны отсутствует значение поля, по которому выполняется фильтрация, такая зона исключается из результата.
Пример запроса — полная модель
GET /api/v1/zones?camera_id=1 HTTP/1.1
Host: api.parktrack.live
Authorization: Bearer <token>
Пример запроса — режим карты
GET /api/v1/zones?bbox=30.30,59.92,30.35,59.97&view=map HTTP/1.1
Host: api.parktrack.live
Authorization: Bearer <token>
Response (200)
- при
view=full— массив объектовZone; - при
view=map— массив объектовZoneMapItem.
Пример ответа (200) — view=full
[
{
"zone_id": 1,
"camera_id": 1,
"zone_type": "parallel",
"capacity": 7,
"occupied": 5,
"free_count": 2,
"confidence": 0.76,
"confidence_level": "medium",
"pay": 0,
"geometry": {
"type": "Polygon",
"coordinates": [
[
[30.309426, 59.955976],
[30.309358, 59.956008],
[30.309979, 59.956231],
[30.310043, 59.956201],
[30.309426, 59.955976]
]
]
},
"image_polygon": [
[45, 23],
[87, 25],
[79, 149],
[32, 145]
],
"partner_id": 10,
"created_by_user_id": 123,
"is_active": true,
"location_type": "street",
"is_accessible": false,
"is_private": false,
"occupancy_updated_at": "2026-04-06T10:10:00Z",
"created_at": "2026-04-06T10:00:00Z",
"updated_at": "2026-04-06T10:10:00Z"
}
]
Пример ответа (200) — view=map
[
{
"zone_id": 1,
"zone_type": "parallel",
"capacity": 7,
"occupied": 5,
"free_count": 2,
"confidence": 0.76,
"confidence_level": "medium",
"pay": 0,
"geometry": {
"type": "Polygon",
"coordinates": [
[
[30.309426, 59.955976],
[30.309358, 59.956008],
[30.309979, 59.956231],
[30.310043, 59.956201],
[30.309426, 59.955976]
]
]
},
"location_type": "street",
"is_accessible": false,
"occupancy_updated_at": "2026-04-06T10:10:00Z",
"is_active": true
}
]
Ошибки
| Код | Тип | Описание |
|---|---|---|
| 401 | Unauthorized | Токен отсутствует, невалиден или истёк. |
| 403 | Forbidden | Недостаточно прав для просмотра зон. |
| 422 | Unprocessable Entity | Валидация не пройдена, например bbox имеет неверный формат. |
5.7 POST /zones/new
Создаёт новую парковочную зону.
Эндпоинт сохранён без изменения пути для совместимости с текущим кодом.
Требуемые разрешения
zones.create
Требуемая область доступа
write_scopeдолжен разрешать создание зоны в выбранной области данных.
Headers
Authorization: Bearer <access_token>Content-Type: application/json
Request body (required)
Обязательные поля
camera_id(integer) — существующий ID камеры.zone_type(string) —parallelилиstandard.capacity(integer,>= 0)pay(integer,>= 0)geometry(GeoJSON Polygon) — геометрия зоны на карте.image_polygon(array[4]) — геометрия зоны в координатах изображения камеры.
Дополнительные поля
location_type(string, optional)is_private(boolean, optional)is_accessible(boolean, optional)
Валидация
camera_idдолжен ссылаться на существующую камеру;geometryдолжен быть корректным GeoJSON Polygon;image_polygonдолжен содержать ровно 4 вершины;- координаты
latitude/longitudeвgeometryдолжны быть в допустимых диапазонах; xиyвimage_polygonдолжны быть>= 0;capacity >= 0,pay >= 0;- сервер может дополнительно валидировать геометрию зоны как невырожденный полигон.
Пример запроса
POST /api/v1/zones/new HTTP/1.1
Host: api.parktrack.live
Content-Type: application/json
Authorization: Bearer <token>
{
"camera_id": 1,
"zone_type": "parallel",
"capacity": 7,
"pay": 0,
"geometry": {
"type": "Polygon",
"coordinates": [
[
[30.309426, 59.955976],
[30.309358, 59.956008],
[30.309979, 59.956231],
[30.310043, 59.956201],
[30.309426, 59.955976]
]
]
},
"image_polygon": [
[45, 23],
[87, 25],
[79, 149],
[32, 145]
],
"location_type": "street",
"is_private": false,
"is_accessible": false
}
Response (201)
zone_id(integer) — идентификатор созданной зоны.
Пример ответа (201)
{
"zone_id": 1
}
Ошибки
| Код | Тип | Описание |
|---|---|---|
| 400 | Bad Request | Невалидное тело запроса. |
| 401 | Unauthorized | Токен отсутствует, невалиден или истёк. |
| 403 | Forbidden | Недостаточно прав для создания зоны. |
| 404 | Not Found | Камера с указанным camera_id не существует. |
| 422 | Unprocessable Entity | Ошибка валидации геометрии, image_polygon, диапазонов или полей. |
Пример ответа (422)
{
"error_description": "Validation error: image_polygon must contain exactly 4 vertices"
}
5.8 GET /zones/<zone_id>
Возвращает данные о конкретной парковочной зоне.
Path-параметры
zone_id(integer, required) — идентификатор зоны.
Требуемые разрешения
zones.view
Требуемая область доступа
read_scopeдолжен включать целевую зону.
Headers
Authorization: Bearer <access_token>
Пример запроса
GET /api/v1/zones/1 HTTP/1.1
Host: api.parktrack.live
Authorization: Bearer <token>
Response (200) — объект Zone
Пример ответа (200)
{
"zone_id": 1,
"camera_id": 1,
"zone_type": "parallel",
"capacity": 7,
"occupied": 5,
"free_count": 2,
"confidence": 0.76,
"confidence_level": "medium",
"pay": 0,
"geometry": {
"type": "Polygon",
"coordinates": [
[
[30.309426, 59.955976],
[30.309358, 59.956008],
[30.309979, 59.956231],
[30.310043, 59.956201],
[30.309426, 59.955976]
]
]
},
"image_polygon": [
[45, 23],
[87, 25],
[79, 149],
[32, 145]
],
"partner_id": 10,
"created_by_user_id": 123,
"is_active": true,
"location_type": "street",
"is_private": false,
"is_accessible": false,
"occupancy_updated_at": "2026-04-06T10:10:00Z",
"created_at": "2026-04-06T10:00:00Z",
"updated_at": "2026-04-06T10:10:00Z"
}
Ошибки
| Код | Тип | Описание |
|---|---|---|
| 401 | Unauthorized | Токен отсутствует, невалиден или истёк. |
| 403 | Forbidden | Недостаточно прав для просмотра зоны. |
| 404 | Not Found | Зона не найдена. |
5.9 PUT /zones/<zone_id>
Обновляет данные парковочной зоны.
Допускается частичное обновление.
Path-параметры
zone_id(integer, required)
Требуемые разрешения
zones.update
Требуемая область доступа
write_scopeдолжен включать целевую зону.
Headers
Authorization: Bearer <access_token>Content-Type: application/json
Request body
camera_id(integer, optional) — ID камеры.zone_type(string, optional) —parallelилиstandard.capacity(integer, optional,>= 0)pay(integer, optional,>= 0)occupied(integer, optional,>= 0,<= capacity)confidence(float, optional,0..1)geometry(GeoJSON Polygon, optional) — геометрия зоны на карте.image_polygon(array[4], optional) — геометрия зоны в координатах изображения камеры.location_type(string, optional)is_private(boolean, optional)is_accessible(boolean, optional)is_active(boolean, optional)
Поля
zone_id,free_count,created_at,updated_at,created_by_user_id,partner_id,occupancy_updated_atизменяются сервером и игнорируются в теле запроса.
Пример запроса — частичное обновление занятости
PUT /api/v1/zones/1 HTTP/1.1
Host: api.parktrack.live
Content-Type: application/json
Authorization: Bearer <token>
{
"occupied": 5,
"confidence": 0.76
}
Пример запроса — обновление геометрии
PUT /api/v1/zones/1 HTTP/1.1
Host: api.parktrack.live
Content-Type: application/json
Authorization: Bearer <token>
{
"geometry": {
"type": "Polygon",
"coordinates": [
[
[30.309426, 59.955976],
[30.309358, 59.956008],
[30.309979, 59.956231],
[30.310043, 59.956201],
[30.309426, 59.955976]
]
]
},
"image_polygon": [
[45, 23],
[87, 25],
[79, 149],
[32, 145]
]
}
Response (200) — объект Zone
Пример ответа (200)
{
"zone_id": 1,
"camera_id": 1,
"zone_type": "parallel",
"capacity": 7,
"occupied": 5,
"free_count": 2,
"confidence": 0.76,
"confidence_level": "medium",
"pay": 0,
"geometry": {
"type": "Polygon",
"coordinates": [
[
[30.309426, 59.955976],
[30.309358, 59.956008],
[30.309979, 59.956231],
[30.310043, 59.956201],
[30.309426, 59.955976]
]
]
},
"image_polygon": [
[45, 23],
[87, 25],
[79, 149],
[32, 145]
],
"partner_id": 10,
"created_by_user_id": 123,
"is_active": true,
"location_type": "street",
"is_private": false,
"is_accessible": false,
"occupancy_updated_at": "2026-04-06T10:10:00Z",
"created_at": "2026-04-06T10:00:00Z",
"updated_at": "2026-04-06T12:00:00Z"
}
Ошибки
| Код | Тип | Описание |
|---|---|---|
| 400 | Bad Request | Невалидное тело запроса. |
| 401 | Unauthorized | Токен отсутствует, невалиден или истёк. |
| 403 | Forbidden | Недостаточно прав для изменения зоны. |
| 404 | Not Found | zone_id не существует, либо указанная камера не найдена. |
| 422 | Unprocessable Entity | Ошибка валидации: capacity, occupied, confidence, geometry или image_polygon. |
Пример ответа (422)
{
"error_description": "Validation error: occupied must be between 0 and capacity"
}
5.10 DELETE /zones/<zone_id>
Удаляет парковочную зону.
Path-параметры
zone_id(integer, required) — идентификатор зоны.
Требуемые разрешения
zones.delete
Требуемая область доступа
delete_scopeдолжен включать целевую зону.
Headers
Authorization: Bearer <access_token>
Пример запроса
DELETE /api/v1/zones/1 HTTP/1.1
Host: api.parktrack.live
Authorization: Bearer <token>
Response (204)
Тело ответа отсутствует.
Ошибки
| Код | Тип | Описание |
|---|---|---|
| 401 | Unauthorized | Токен отсутствует, невалиден или истёк. |
| 403 | Forbidden | Недостаточно прав для удаления зоны. |
| 404 | Not Found | Зона не найдена. |
| 409 | Conflict | Удаление невозможно из-за зависимостей. |
Пример ответа (404)
{
"error_description": "Zone not found"
}
5.11 Требования к другим разделам API
Если эндпоинт другого раздела работает с зоной как с геообъектом, рекомендуется использовать:
zone_id(integer)geometry(GeoJSON Polygon)
Если эндпоинт другого раздела работает с зоной как с сущностью, связанной с камерой и разметкой, рекомендуется использовать:
zone_id(integer)camera_id(integer)image_polygon(array[4])
Если фронтенду или служебному коду требуется временно восстановить старое представление из четырёх опорных точек, оно может быть получено:
- из внешнего контура
geometryдля координат на карте; - из
image_polygonдля координат в изображении камеры.