### Введение
Документ описывает предложение по реализации нового слоя для отображения дорожных знаков для НЯК.

### Требования
Целевое состояние:
В НЯК появится автоматически обновляемый по свежим проездам слой объектов-знаков.
Знаки описываются следующими атрибутами:
- тип
- признак временности
- позиция
- направление
- снимки, на которых знак виден
- время жизни объекта (дата обнаружения - дата исчезновения)
- снимки, которые подтверждают исчезновение объекта
- множество информационных табличек, ассоциированных со знаком.

Пользователи смогут:
- просматривать знаки на карте (для удобства потребуются пресеты слоев по отдельным группам знаков: запрещающие, предписывающие, влияющие на грузовой транспорт)
- просматривать снимки, подтверждающие существование и исчезновение знака.

### Верхнеуровневое описание решения
Регулярный процесс формирует и публикует датасет с информацией о дорожных знаках, найденных на фотографиях MRC и панорамах.
Новые ручки в MRC browser позволяют загружать тайлы слоя дорожных знаков, а также получать подробную информацию о каждом знаке.
Новый слой будет применим для любых объектов, распознанных на фотографиях MRC и панорамах. На первом этапе он будет использоваться только для дорожных знаков с возможностью расширения в будущем на другие [типы объектов](https://a.yandex-team.ru/arcadia/maps/wikimap/mapspro/services/mrc/libs/db/include/eye/object_attrs.h?rev=r9636231#L11).
Параметры ручек позволят запрашивать знаки с фильтрацией по типам. Определенные наборы типов будут объеденины в подслои, удобные для задач картографов и других пользователей.

### Датасет с дорожными знаками
Для быстрого доступа к слою знаков будет генерироваться новый датасет `yandex-maps-mrc-objects`. Получателем датасета будет `mrc-browser`. Формат датасета: `fb`.
При формировании датасета из БД будут загружаться все объекты `eye.object` с типом `sign`.
Через цепочку связей
`eye.object -> (eye.detection, eye.primary_detection_relation) -> eye.detection_group -> eye.frame -> eye.feature_to_frame, eye.panorama_to_frame`
будет загружаться информация о фотографиях и панорамах, на которых задетектирован знак.
Данные будут записываться в несколько файлов:

Файлы:
- `rtree.fb` - Пространственное дерево с идентификаторами объектов.
- `objects.fb` - подробная информация об объектах.
- `object_featres.fb` - связь объекта с фотографиями, на которых он виден (может быть реализовано как список внутри `objects.fb`).
- `object_panoramas.fb` - связь объекта с панорамами, на которых он виден (может быть реализовано как список внутри `objects.fb`).

Поля объекта-знака в датасете:

| Field                    | Type                 | Bytes    |
|--------------------------|----------------------|----------|
| object_id                | ulong(key)           | 8        |
| object_type              | ushort               | 2        |
| sign_type                | ushort               | 2        |
| sign_text                | string               | variable |
| latitude                 | float                | 4        |
| longitude                | float                | 4        |
| heading                  | ushort               | 2        | 
| detected_since           | ulong                | 8        |
| disappeared_since        | ulong                | 8        |
| is_temporary             | byte                 | 1        |
| [relations]              | array                | ?        |
| **TOTAL**                |                      | 39       |

`sign_text` содержит опциональную текстовую подпись на знаке. Он будет использоваться, например, указания числового значения на знаках ограничения массы и высоты.
Массив `[relations]` содержит список идентификаторов связанных с данным знаком объектов - 
в данном случае информационных табличек, относящихся к знаку.
Хранение информационных табличек может быть реализовано двумя способами:
1. Экспортировать все таблички в основной датасет. Массив `relations` будет хранить идентификаторы связанных объектов и тип связи.
2. Не экспортировать таблички. Массив `relations` в основной датасет. Массив `relations` будет хранить лишь типы табличек.
Вариант 1 увеличит размер датасета, но имеет следующие преимущества:
  - позволит иметь более полную информацию, в частности, видеть фотографии табличек.
  - позволит отслеживать факт исчезновения таблички единообразно с исчезновением остальных знаков.
Предпочтительный вариант можно выбрать в ходе реализации исходя из объема получившегося датасета.

Связь объект-фотография:

| Field                    | Type                 | Bytes |
|--------------------------|----------------------|-------|
| object_id                | ulong(key)           | 8     |
| feature_id               | ulong                | 8     |
| orientation              | byte                 | 1
| bbox                     | 4 * ushort           | 8     |
| **TOTAL**                |                      | 25    |

Связь объект-панорама:

| Field                    | Type                 | Bytes |
|--------------------------|----------------------|-------|
| object_id                | ulong(key)           | 8     |
| oid                      | string               | 40    |
| width                    | uint                 | 4     |
| height                   | uint                 | 4     |
| latitude                 | float                | 4     |
| longitude                | float                | 4     |
| heading                  | ushort               | 2     |
| tilt                     | ushort               | 2     |
| horizontal_fov           | ushort               | 2     |
| bbox                     | 4 * ushort           | 8     |
| **TOTAL**                |                      | 78    |

Поле `bbox` будет содержать xMin, yMin, xMax, yMax знака в пикселях в координатах оригинальной фотографии (без учета orientation).
В датасет будет выгружаться информация об ограниченном количестве фотографий и панорам, где виден/исчез объект.
Фотографии, подтверждающие исчезновение объекта будут добавлены в тот же список, у них будет отсутствовать поле `bbox`.

Оценка размера:
- `rtree.fb` ~ 0.5 Гб
- `objects.fb` ~ 1.5 Гб (без учета связей с информационными табличками)
- `object_feautres.fb` ~ 4.5 Гб 
- `object_panoramas.fb` ~ 1.5 Гб

Формирование и публикация датасета может быть встроена в существующий процесс генерации экспорта либо существовать как отдельная длинная задача.
Вычитывать данные о фотографиях предлагается из экспортного датасета.
Преимущество встраивания в существующий процесс состоит в использовании наиболее актуальной версией датасета с фотографиями.
Преимущество отдельного процесса-генератора в том, что не будет удлинняться процесс экспорта и переключения датасета.
TBD.

### Новое API в mrc-browser
#### GET /objects/tiles
Получить тайл слоя объектов в растровом или векторном формате.

| **Method** | GET |
|------------|-----|
| **Parameters** |
| x | tile x coordinate |
| y | tile y coordinate |
| z | tile z coordinate |
| zmin | z min for multi-zoom |
| zmax | z max for multi-zoom |
| l | layer: `ts` for traffic signs |
| sl | sub-layer |
| detected_since | return objects detected since this date |
| disappeared_since | return objects disappeared since this date |
| format | Preferred format: `png/protobuf/text` |
| **Response body** | Tile in requested format |

#### GET /objects/hotspot
Хотспоты с краткой информацией об объектах в тайле.

| **Method** | GET |
|------------|-----|
| **Parameters** |
| x | tile x coordinate |
| y | tile y coordinate |
| z | tile z coordinate |
| zmin | z min for multi-zoom |
| zmax | z max for multi-zoom |
| l | layer: `ts` for traffic signs |
| sl | sub-layer |
| detected_since | return objects detected since this date |
| disappeared_since | return objects disappeared since this date |
| **Response body** | `ObjectHotspotResponse` |

##### Примерный вариант ответа `ObjectHotspotResponse`:
```json
{
    "data": {
        "type": "FeatureCollection",
        "features": [
            {
                "type": "Feature",
                "geometry": {
                    "coordinates": [
                        44.013757666,
                        56.322418729
                    ],
                    "type": "Point"
                },
                "properties": {
                    "id": "123456",
                    "objectType": "traffic_sign",
                    "signType": "prohibitory_no_vehicles",
                    "pos": [
                        44.013757666,
                        56.322418729
                    ],
                    "heading": 120,
                    "detectedSince": "2021-01-01T12:00:00",
                    "temporary": false
                }
            }
        ]
    }
}
```

#### GET /objects/$/get
Возвращает подробную информацию об объекте-знаке.

| **Method** | GET |
|------------|-----|
| **Parameters** |
| id | object identifier |
| **Response body** | `ObjectResponse` |

Ответ будет содержать:
1. Основную информацию о знаке (тип, позиция, направление, дата обнаружения, дата исчезновения, признак временности, и т.п.)
2. Список фотографий/панорам, на которых виден знак. Для каждой фотографии: дата, позиция, направление, bbox знака, url изображения.
В тот же список можно будет добавлять фотографии, подтверждающие отсутствие знака. Отличить такие фотографии можно будет по дате снимка,
которая в этом случае будет больше либо равна дате исчезновения знака.
3. Список объектов-связей - будет использоваться для указания связанных со знаком информационных табличек.
Для каждого связанного объекта будет указан его тип, и идентификатор. По идентификатору можно будет так же запросить подробную информацию.

##### Примерный вариант ответа `ObjectResponse`:
```json
{
    "type": "Feature",
    "geometry": {
        "coordinates": [
            44.013757666,
            56.322418729
        ],
        "type": "Point"
    },
    "properties": {
        "id": "123456",
        "objectType": "traffic_sign",
        "signType": "prohibitory_no_vehicles",
        "pos": [
            44.013757666,
            56.322418729
        ],
        "heading": 120,
        "detectedSince": "2021-01-01T12:00:00",
        "temporary": false,
        "photos": {
            "type": "FeatureCollection",
            "features": [
                {
                    "type": "Feature",
                    "geometry": {
                        "coordinates": [
                            44.013112,
                            56.322603
                        ],
                        "type": "Point"
                    },
                    "properties":  {
                        "pos": [
                            44.013112,
                            56.322603
                        ],
                        "heading": 115,
                        "created_at": "2020-12-12T15:15:15",
                        "box": {
                            "minX": 400,
                            "minY": 200,
                            "maxX": 500,
                            "maxY": 300
                        },
                        "imageFull": {
                            "url": "https://mrc-browser.maps.yandex.net/feature/9876543/image",
                            "width": 1600,
                            "height": 1200
                        },
                        "imagePreview": {
                            "url": "https://mrc-browser.maps.yandex.net/feature/9876543/thumbnail",
                            "width": 64,
                            "height": 64
                        },
                        "MrcPhotoProperties": {
                            "id": "9876543"
                        }
                    }
                },
                {
                    "type": "Feature",
                    "geometry": {
                        "coordinates": [
                            44.013112,
                            56.322603
                        ],
                        "type": "Point"
                    },
                    "properties":  {
                        "pos": [
                            44.013150,
                            56.322579
                        ],
                        "heading": 100,
                        "created_at": "2020-10-12T09:00:00",
                        "box": {
                            "minX": 500,
                            "minY": 200,
                            "maxX": 600,
                            "maxY": 300
                        },
                        "imageFull": {
                            "url": "https://mrc-browser.maps.yandex.net/v2/pano/123456789/image?heading=100&tilt=0&hfov=90&size=1200,1200",
                            "width": 1200,
                            "height": 1200
                        },
                        "imagePreview": {
                            "url": "https://mrc-browser.maps.yandex.net/v2/pano/123456789/image?heading=100&tilt=0&hfov=90&size=100,100",
                            "width": 100,
                            "height": 100
                        },
                        "PanoramaProperties": {
                            "oid": "123456789",
                            "tilt": 0,
                            "hFov": 90
                        }
                    }
                }
            ]
        },
        "related_objects": [
            {
                "id": "1026543",
                "objectType": "traffic_sign",
                "signType": "information_in_zone"
            }
        ]
    }
}
```


### Подслои знаков
Для запроса слоя дорожных знаков в ручки `/objects/tile` и `/objects/hotspot` будет передаваться параметр `l=ts` (traffic signs).
Для указания конкретных типов знаков предполагается разделение слоя знаков на подслои.
Можно рассмотреть три варианта разделения:
1. Передавать параметр "подслой" (`sl` - sublayer) соответствующий определенной категории знаков по ПДД (предписывающие, запрещающие, приоритета и т.п.).
Пример: `GET /objects/tile?l=ts&sl=prohibitory&...`
Такое решение является негибким, может привести к слишком большому количеству отображаемых знаков.
2. Передавать параметр "подслой" (`sl` - sublayer). Каждое значение параметра `sl` будет соответствовать подмножеству знаков, необходимых картографам для определенного рабочего сценария (напр. разметки дворовых зон, полосности, зон скоростных ограничений и т.д.).
Пример: `GET /objects/tile?l=ts&sl=speed_limits&...`
3. Передавать в параметре "типы знаков" (`sign_types`) явный список типов знаков, необходимых для отображения. В этом случае "пресеты" знаков могут определяться на стороне НЯК и их изменение не потребует изменений в реализации mrc-бекенда. Каждому типу знака будет присвоен числовой либо строковый идентификатор.
Пример: `GET /objects/tile?l=ts&sign_types=10,25,32,40&...`. Недостаток этого варианта в том, что он не позволяет задать более сложные условия фильтрации, например "знак + информационная табличка".

Будет реализован вариант 2.

Предварительный список типов подслоев (получен от картографов, см. https://st.yandex-team.ru/MAPSMRC-3945#62bf1d4901c17f6e71cf7a73):
| **Sublayer name**     | **Description** |
|-----------------------|-----------------|
| constructive_features | Конструктивные особенности |
| routing_automotive    | Автомобильная маршрутизация |
| routing_pdst_bike     | Пешеходная и велосипедная маршрутизация |
| routing_heavy_vehicle | Грузовая маршрутизация |
| speed_limits          | Скоростные ограничения |
| parking               | Парковочная инфраструктура |
| public_transport      | Общественный транспорт |
| traffic_lanes         | Полосность |

### Дизайн
Начальная версия дизайна слоя знаков будет создана в [Картографе](https://cartograph.maps.yandex-team.ru) и будет храниться рядом с кодом mrc-browser.

