Перейти к основному содержимому

5. Parking Zones

Раздел описывает парковочные зоны, их геометрию на карте, геометрию в изображении камеры и текущую оценку занятости.

5.1 Совместимость с текущей реализацией

Раздел сохраняет существующие основные эндпоинты:

  • GET /zones
  • POST /zones/new
  • GET /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 к зонам партнёра.

Общие ошибки

КодТипОписание
400Bad RequestНевалидный JSON или неверные типы полей.
401UnauthorizedТокен отсутствует, невалиден, истёк или сессия завершена.
403ForbiddenУ пользователя недостаточно прав для выполнения действия.
404Not FoundЗона или связанная камера не найдена.
409ConflictКонфликт данных.
415Unsupported Media TypeНеверный Content-Type. Используй application/json.
422Unprocessable EntityОшибка валидации.
500Internal Server ErrorНеобработанная ошибка сервера.
503Service 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_low
    • low
    • medium
    • high

Поля 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
}
]

Ошибки

КодТипОписание
401UnauthorizedТокен отсутствует, невалиден или истёк.
403ForbiddenНедостаточно прав для просмотра зон.
422Unprocessable 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
}

Ошибки

КодТипОписание
400Bad RequestНевалидное тело запроса.
401UnauthorizedТокен отсутствует, невалиден или истёк.
403ForbiddenНедостаточно прав для создания зоны.
404Not FoundКамера с указанным camera_id не существует.
422Unprocessable 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"
}

Ошибки

КодТипОписание
401UnauthorizedТокен отсутствует, невалиден или истёк.
403ForbiddenНедостаточно прав для просмотра зоны.
404Not 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"
}

Ошибки

КодТипОписание
400Bad RequestНевалидное тело запроса.
401UnauthorizedТокен отсутствует, невалиден или истёк.
403ForbiddenНедостаточно прав для изменения зоны.
404Not Foundzone_id не существует, либо указанная камера не найдена.
422Unprocessable 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)
Тело ответа отсутствует.

Ошибки

КодТипОписание
401UnauthorizedТокен отсутствует, невалиден или истёк.
403ForbiddenНедостаточно прав для удаления зоны.
404Not FoundЗона не найдена.
409ConflictУдаление невозможно из-за зависимостей.

Пример ответа (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 для координат в изображении камеры.