Polygon API Documentation
Polygon API Documentation
Overview
Polygon API предоставляет возможности для создания полигонов доставки и их привязки к методам доставки OZON. API включает 4 метода для управления географическими зонами доставки и настройки времени доставки.
Key Features
- 🗺️ Создание полигонов доставки - определение географических зон с помощью координат GeoJSON
- 📍 Привязка к методам доставки - связывание полигонов с конкретными способами доставки
- ⏱️ Настройка времени доставки - указание времени доставки для каждого полигона
- 🏪 Геопозиционирование складов - привязка полигонов к местоположению складов
- 🎯 Зональная доставка - создание различных зон с разным временем доставки
- 🛠️ Интеграция с GeoJSON.io - использование онлайн-инструментов для создания координат
Available Methods
Main Methods
createDeliveryPolygon()
Создает полигон для зоны доставки с указанием координат в формате GeoJSON.
// Создать простой прямоугольный полигон для центра Москвы
const moscowPolygon = await polygonApi.createDeliveryPolygon({
coordinates: `[[[
[55.7558, 37.6176], // Красная площадь
[55.7558, 37.7176], // Восточная граница
[55.8558, 37.7176], // Северо-восточный угол
[55.8558, 37.6176], // Северная граница
[55.7558, 37.6176] // Замыкание полигона
]]]`
});
console.log(`Создан полигон с ID: ${moscowPolygon.polygon_id}`);
bindPolygonToDeliveryMethod()
Привязывает созданные полигоны к методу доставки с указанием времени доставки и местоположения склада.
// Привязать полигон к экспресс-доставке
await polygonApi.bindPolygonToDeliveryMethod({
delivery_method_id: 123,
warehouse_location: {
lat: "55.7558", // Широта склада
lon: "37.6176" // Долгота склада
},
polygons: [{
polygon_id: moscowPolygon.polygon_id!,
time: 120 // 2 часа доставки
}]
});
TypeScript Interfaces
Request Types
interface PolygonCreateRequest {
coordinates: string; // GeoJSON строка с координатами полигона
}
interface PolygonBindRequest {
delivery_method_id: number;
warehouse_location: WarehouseLocation;
polygons: PolygonBinding[];
}
interface WarehouseLocation {
lat: string; // Широта склада
lon: string; // Долгота склада
}
interface PolygonBinding {
polygon_id: number;
time: number; // Время доставки в минутах
}
Response Types
interface PolygonCreateResponse {
polygon_id?: number;
success?: boolean;
message?: string;
}
// bindPolygonToDeliveryMethod возвращает void при успешном выполнении
Usage Examples
Basic Polygon Management
import { OzonApi } from 'bmad-ozon-seller-api';
const ozonApi = new OzonApi({
clientId: 'your-client-id',
apiKey: 'your-api-key'
});
// Создание простого полигона для зоны доставки
async function createDeliveryZone() {
try {
// Создать полигон для центра города
const polygon = await ozonApi.polygon.createDeliveryPolygon({
coordinates: `[[[
[55.7400, 37.6000],
[55.7400, 37.6800],
[55.7800, 37.6800],
[55.7800, 37.6000],
[55.7400, 37.6000]
]]]`
});
if (!polygon.polygon_id) {
throw new Error('Не удалось создать полигон');
}
console.log(`✅ Полигон создан с ID: ${polygon.polygon_id}`);
// Привязать полигон к методу быстрой доставки
await ozonApi.polygon.bindPolygonToDeliveryMethod({
delivery_method_id: 456, // ID быстрой доставки
warehouse_location: {
lat: "55.7558",
lon: "37.6176"
},
polygons: [{
polygon_id: polygon.polygon_id,
time: 90 // 1.5 часа доставки
}]
});
console.log('✅ Полигон успешно привязан к методу доставки');
return polygon.polygon_id;
} catch (error) {
console.error('❌ Ошибка создания зоны доставки:', error);
throw error;
}
}
// Использование
const zoneId = await createDeliveryZone();
Advanced Multi-Zone Setup
// Создание нескольких зон с разным временем доставки
async function setupMultiZoneDelivery() {
const zones = [
{
name: 'Центр Москвы',
coordinates: `[[[
[55.7400, 37.5800],
[55.7400, 37.6400],
[55.7700, 37.6400],
[55.7700, 37.5800],
[55.7400, 37.5800]
]]]`,
deliveryTime: 60 // 1 час
},
{
name: 'Ближние районы',
coordinates: `[[[
[55.7000, 37.5000],
[55.7000, 37.7000],
[55.8000, 37.7000],
[55.8000, 37.5000],
[55.7000, 37.5000]
]]]`,
deliveryTime: 180 // 3 часа
},
{
name: 'Дальние районы',
coordinates: `[[[
[55.6000, 37.4000],
[55.6000, 37.8000],
[55.9000, 37.8000],
[55.9000, 37.4000],
[55.6000, 37.4000]
]]]`,
deliveryTime: 360 // 6 часов
}
];
const results = [];
for (const zone of zones) {
try {
// Создать полигон
const polygon = await ozonApi.polygon.createDeliveryPolygon({
coordinates: zone.coordinates
});
if (polygon.polygon_id) {
// Привязать к методу доставки
await ozonApi.polygon.bindPolygonToDeliveryMethod({
delivery_method_id: 789, // ID основного метода доставки
warehouse_location: {
lat: "55.7558",
lon: "37.6176"
},
polygons: [{
polygon_id: polygon.polygon_id,
time: zone.deliveryTime
}]
});
results.push({
name: zone.name,
polygonId: polygon.polygon_id,
deliveryTime: zone.deliveryTime,
status: 'success'
});
console.log(`✅ ${zone.name}: полигон ${polygon.polygon_id}, доставка ${zone.deliveryTime} мин`);
}
// Небольшая задержка между запросами
await new Promise(resolve => setTimeout(resolve, 500));
} catch (error) {
results.push({
name: zone.name,
error: error.message,
status: 'failed'
});
console.error(`❌ Ошибка создания зоны ${zone.name}:`, error);
}
}
return results;
}
// Использование
const zoneResults = await setupMultiZoneDelivery();
console.log('Результаты настройки зон:', zoneResults);
Complex Scenarios
Delivery Zone Management System
Класс для комплексного управления зонами доставки:
class DeliveryZoneManager {
constructor(private ozonApi: OzonApi) {}
/**
* Создание полигона-донута (с отверстием)
*/
async createDonutPolygon(
outerBounds: number[][],
innerBounds: number[][],
deliveryMethodId: number,
deliveryTime: number,
warehouseLat: string,
warehouseLon: string
) {
try {
// Создать полигон с отверстием (donut shape)
const coordinates = `[${JSON.stringify(outerBounds)}, ${JSON.stringify(innerBounds)}]`;
const polygon = await this.ozonApi.polygon.createDeliveryPolygon({
coordinates
});
if (!polygon.polygon_id) {
throw new Error('Не удалось создать полигон-донут');
}
// Привязать к методу доставки
await this.ozonApi.polygon.bindPolygonToDeliveryMethod({
delivery_method_id: deliveryMethodId,
warehouse_location: {
lat: warehouseLat,
lon: warehouseLon
},
polygons: [{
polygon_id: polygon.polygon_id,
time: deliveryTime
}]
});
return polygon.polygon_id;
} catch (error) {
console.error('Ошибка создания полигона-донута:', error);
throw error;
}
}
/**
* Создание радиального полигона (приближение к кругу)
*/
createCircularPolygon(centerLat: number, centerLon: number, radiusKm: number, points = 12) {
const coordinates: number[][] = [];
const earthRadius = 6371; // км
for (let i = 0; i <= points; i++) {
const angle = (i * 2 * Math.PI) / points;
// Вычисление координат точки на окружности
const deltaLat = (radiusKm / earthRadius) * Math.cos(angle) * (180 / Math.PI);
const deltaLon = (radiusKm / earthRadius) * Math.sin(angle) * (180 / Math.PI) / Math.cos(centerLat * Math.PI / 180);
const lat = centerLat + deltaLat;
const lon = centerLon + deltaLon;
coordinates.push([lat, lon]);
}
return `[${JSON.stringify(coordinates)}]`;
}
/**
* Создание зоны быстрой доставки в радиусе от склада
*/
async setupExpressDeliveryZone(
warehouseLat: number,
warehouseLon: number,
radiusKm: number,
deliveryMethodId: number,
deliveryTimeMinutes: number
) {
try {
const coordinates = this.createCircularPolygon(warehouseLat, warehouseLon, radiusKm);
const polygon = await this.ozonApi.polygon.createDeliveryPolygon({
coordinates: `[${coordinates}]`
});
if (!polygon.polygon_id) {
throw new Error('Не удалось создать зону экспресс-доставки');
}
await this.ozonApi.polygon.bindPolygonToDeliveryMethod({
delivery_method_id: deliveryMethodId,
warehouse_location: {
lat: warehouseLat.toString(),
lon: warehouseLon.toString()
},
polygons: [{
polygon_id: polygon.polygon_id,
time: deliveryTimeMinutes
}]
});
console.log(`✅ Создана зона экспресс-доставки радиусом ${radiusKm}км, время ${deliveryTimeMinutes}мин`);
return polygon.polygon_id;
} catch (error) {
console.error('Ошибка создания зоны экспресс-доставки:', error);
throw error;
}
}
/**
* Создание зон доставки по районам города
*/
async setupCityDistrictDelivery(districts: CityDistrict[]) {
const results: DistrictResult[] = [];
for (const district of districts) {
try {
const polygon = await this.ozonApi.polygon.createDeliveryPolygon({
coordinates: district.coordinates
});
if (polygon.polygon_id) {
await this.ozonApi.polygon.bindPolygonToDeliveryMethod({
delivery_method_id: district.deliveryMethodId,
warehouse_location: district.warehouseLocation,
polygons: [{
polygon_id: polygon.polygon_id,
time: district.deliveryTime
}]
});
results.push({
district: district.name,
polygonId: polygon.polygon_id,
status: 'success',
deliveryTime: district.deliveryTime
});
console.log(`✅ Район ${district.name}: полигон ${polygon.polygon_id}`);
}
} catch (error) {
results.push({
district: district.name,
status: 'failed',
error: error.message
});
console.error(`❌ Ошибка настройки района ${district.name}:`, error);
}
// Задержка между запросами
await new Promise(resolve => setTimeout(resolve, 200));
}
return results;
}
/**
* Массовое создание полигонов
*/
async createBulkPolygons(polygonConfigs: PolygonConfig[], batchSize = 5) {
const results: BulkPolygonResult[] = [];
for (let i = 0; i < polygonConfigs.length; i += batchSize) {
const batch = polygonConfigs.slice(i, i + batchSize);
const batchPromises = batch.map(async (config) => {
try {
const polygon = await this.ozonApi.polygon.createDeliveryPolygon({
coordinates: config.coordinates
});
if (polygon.polygon_id) {
await this.ozonApi.polygon.bindPolygonToDeliveryMethod({
delivery_method_id: config.deliveryMethodId,
warehouse_location: config.warehouseLocation,
polygons: [{
polygon_id: polygon.polygon_id,
time: config.deliveryTime
}]
});
return {
name: config.name,
polygonId: polygon.polygon_id,
status: 'success' as const
};
}
throw new Error('Polygon ID не получен');
} catch (error) {
return {
name: config.name,
status: 'failed' as const,
error: error.message
};
}
});
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
console.log(`Обработан batch ${Math.floor(i/batchSize) + 1}/${Math.ceil(polygonConfigs.length/batchSize)}`);
// Задержка между батчами
if (i + batchSize < polygonConfigs.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
return results;
}
}
// Интерфейсы для системы управления
interface CityDistrict {
name: string;
coordinates: string;
deliveryMethodId: number;
deliveryTime: number;
warehouseLocation: WarehouseLocation;
}
interface DistrictResult {
district: string;
polygonId?: number;
status: 'success' | 'failed';
deliveryTime?: number;
error?: string;
}
interface PolygonConfig {
name: string;
coordinates: string;
deliveryMethodId: number;
deliveryTime: number;
warehouseLocation: WarehouseLocation;
}
interface BulkPolygonResult {
name: string;
polygonId?: number;
status: 'success' | 'failed';
error?: string;
}
// Использование системы управления зонами
const zoneManager = new DeliveryZoneManager(ozonApi);
// Создание зоны экспресс-доставки
const expressZoneId = await zoneManager.setupExpressDeliveryZone(
55.7558, 37.6176, // Координаты склада в Москве
15, // Радиус 15 км
123, // ID экспресс-доставки
120 // 2 часа
);
// Создание полигона-донута (доставляем везде кроме центра)
const donutZoneId = await zoneManager.createDonutPolygon(
[ // Внешние границы (большой прямоугольник)
[55.6500, 37.4500],
[55.6500, 37.7500],
[55.8500, 37.7500],
[55.8500, 37.4500],
[55.6500, 37.4500]
],
[ // Внутренние границы (исключаемая зона в центре)
[55.7400, 37.5800],
[55.7400, 37.6400],
[55.7700, 37.6400],
[55.7700, 37.5800],
[55.7400, 37.5800]
],
456, // ID метода доставки
240, // 4 часа доставки
"55.7558", "37.6176"
);
// Настройка доставки по районам
const districts: CityDistrict[] = [
{
name: 'Центральный административный округ',
coordinates: `[[[55.7400, 37.5800], [55.7400, 37.6600], [55.7700, 37.6600], [55.7700, 37.5800], [55.7400, 37.5800]]]`,
deliveryMethodId: 789,
deliveryTime: 60,
warehouseLocation: { lat: "55.7558", lon: "37.6176" }
},
{
name: 'Северный административный округ',
coordinates: `[[[55.7700, 37.5000], [55.7700, 37.7000], [55.8500, 37.7000], [55.8500, 37.5000], [55.7700, 37.5000]]]`,
deliveryMethodId: 789,
deliveryTime: 120,
warehouseLocation: { lat: "55.7558", lon: "37.6176" }
}
];
const districtResults = await zoneManager.setupCityDistrictDelivery(districts);
console.log('Результаты настройки районов:', districtResults);
GeoJSON Helper Functions
Вспомогательные функции для работы с координатами:
class GeoJSONHelper {
/**
* Создание прямоугольного полигона
*/
static createRectangle(
minLat: number, minLon: number,
maxLat: number, maxLon: number
): string {
return `[[[
[${minLat}, ${minLon}],
[${minLat}, ${maxLon}],
[${maxLat}, ${maxLon}],
[${maxLat}, ${minLon}],
[${minLat}, ${minLon}]
]]]`;
}
/**
* Создание полигона вокруг точки с заданным отступом
*/
static createBoundingBox(
centerLat: number, centerLon: number,
offsetDegrees: number
): string {
const minLat = centerLat - offsetDegrees;
const maxLat = centerLat + offsetDegrees;
const minLon = centerLon - offsetDegrees;
const maxLon = centerLon + offsetDegrees;
return this.createRectangle(minLat, minLon, maxLat, maxLon);
}
/**
* Валидация координат полигона
*/
static validatePolygonCoordinates(coordinates: string): boolean {
try {
const parsed = JSON.parse(coordinates);
// Проверяем что это массив
if (!Array.isArray(parsed)) return false;
// Проверяем структуру [[[lat, lon], [lat, lon], ...]]
for (const ring of parsed) {
if (!Array.isArray(ring)) return false;
for (const point of ring) {
if (!Array.isArray(point) || point.length !== 2) return false;
if (typeof point[0] !== 'number' || typeof point[1] !== 'number') return false;
// Проверяем диапазоны координат
const [lat, lon] = point;
if (lat < -90 || lat > 90 || lon < -180 || lon > 180) return false;
}
// Проверяем что первая и последняя точки совпадают (замкнутый полигон)
const firstPoint = ring[0];
const lastPoint = ring[ring.length - 1];
if (firstPoint[0] !== lastPoint[0] || firstPoint[1] !== lastPoint[1]) return false;
// Минимум 4 точки (3 угла + замыкающая)
if (ring.length < 4) return false;
}
return true;
} catch {
return false;
}
}
/**
* Получение центра полигона (приблизительно)
*/
static getPolygonCenter(coordinates: string): { lat: number; lon: number } {
try {
const parsed = JSON.parse(coordinates);
const ring = parsed[0]; // Берем первое кольцо
let sumLat = 0;
let sumLon = 0;
let count = 0;
for (const point of ring) {
sumLat += point[0];
sumLon += point[1];
count++;
}
return {
lat: sumLat / count,
lon: sumLon / count
};
} catch (error) {
throw new Error('Невозможно определить центр полигона: ' + error.message);
}
}
/**
* Конвертация полигона из различных форматов
*/
static convertToGeoJSONString(input: any): string {
if (typeof input === 'string') {
// Уже строка, проверяем валидность
if (this.validatePolygonCoordinates(input)) {
return input;
}
throw new Error('Неверный формат строки координат');
}
if (Array.isArray(input)) {
// Массив координат, конвертируем в строку
const coordinates = JSON.stringify(input);
if (this.validatePolygonCoordinates(coordinates)) {
return coordinates;
}
throw new Error('Неверный формат массива координат');
}
throw new Error('Неподдерживаемый формат входных данных');
}
}
// Использование GeoJSON Helper
const helper = GeoJSONHelper;
// Создать прямоугольник
const rectCoords = helper.createRectangle(55.7, 37.6, 55.8, 37.7);
// Создать зону вокруг точки
const boundingBox = helper.createBoundingBox(55.7558, 37.6176, 0.05);
// Валидировать координаты
const isValid = helper.validatePolygonCoordinates(rectCoords);
console.log('Координаты валидны:', isValid);
// Найти центр полигона
const center = helper.getPolygonCenter(rectCoords);
console.log('Центр полигона:', center);
Error Handling
// Комплексная обработка ошибок при работе с полигонами
async function safePolygonOperations() {
try {
// Валидация координат перед отправкой
const coordinates = `[[[55.7400, 37.5800], [55.7400, 37.6400], [55.7700, 37.6400], [55.7700, 37.5800], [55.7400, 37.5800]]]`;
if (!GeoJSONHelper.validatePolygonCoordinates(coordinates)) {
throw new Error('Неверный формат координат полигона');
}
// Создание полигона
const polygon = await ozonApi.polygon.createDeliveryPolygon({
coordinates
});
if (!polygon.polygon_id) {
throw new Error('Не получен ID созданного полигона');
}
// Привязка к методу доставки
await ozonApi.polygon.bindPolygonToDeliveryMethod({
delivery_method_id: 123,
warehouse_location: {
lat: "55.7558",
lon: "37.6176"
},
polygons: [{
polygon_id: polygon.polygon_id,
time: 120
}]
});
return polygon.polygon_id;
} catch (error) {
if (error.code === 'INVALID_COORDINATES') {
console.error('Неверные координаты полигона:', error.message);
} else if (error.code === 'DELIVERY_METHOD_NOT_FOUND') {
console.error('Метод доставки не найден:', error.message);
} else if (error.code === 'WAREHOUSE_LOCATION_INVALID') {
console.error('Неверные координаты склада:', error.message);
} else if (error.code === 'POLYGON_TOO_LARGE') {
console.error('Полигон слишком большой:', error.message);
} else if (error.code === 'POLYGON_TOO_SMALL') {
console.error('Полигон слишком маленький:', error.message);
} else {
console.error('Неожиданная ошибка:', error);
}
throw error;
}
}
Best Practices
1. Создание качественных полигонов
const bestPractices = {
// Используйте https://geojson.io для визуального создания полигонов
useGeoJSONIO: true,
// Минимальные требования к полигону
minPoints: 4, // Минимум 3 угла + замыкающая точка
// Замыкание полигона (первая и последняя точки должны совпадать)
closedPolygon: true,
// Порядок точек (по часовой стрелке для внешнего кольца)
clockwiseOrder: true,
// Разумные размеры полигона
maxAreaKmSq: 10000, // Не более 10,000 км²
minAreaKmSq: 1, // Не менее 1 км²
// Валидация координат
validateBeforeSend: true
};
2. Оптимизация времени доставки
const deliveryTimeStrategy = {
// Экспресс-доставка (в пределах 10 км от склада)
express: {
radiusKm: 10,
timeMinutes: 60
},
// Быстрая доставка (10-25 км от склада)
fast: {
radiusKm: 25,
timeMinutes: 180
},
// Обычная доставка (25-50 км от склада)
standard: {
radiusKm: 50,
timeMinutes: 360
},
// Экономичная доставка (более 50 км)
economy: {
radiusKm: 100,
timeMinutes: 1440 // 24 часа
}
};
3. Кэширование полигонов
class PolygonCache {
private cache = new Map<string, number>();
async getOrCreatePolygon(coordinates: string): Promise<number> {
const key = this.hashCoordinates(coordinates);
const cached = this.cache.get(key);
if (cached) {
console.log('Использован кэшированный полигон:', cached);
return cached;
}
const polygon = await ozonApi.polygon.createDeliveryPolygon({ coordinates });
if (polygon.polygon_id) {
this.cache.set(key, polygon.polygon_id);
return polygon.polygon_id;
}
throw new Error('Не удалось создать полигон');
}
private hashCoordinates(coordinates: string): string {
// Простой хеш для координат (в продакшене используйте более надежный)
return btoa(coordinates).replace(/[^a-zA-Z0-9]/g, '').substring(0, 32);
}
}
4. Мониторинг зон доставки
class DeliveryZoneMonitor {
constructor(private ozonApi: OzonApi) {}
async validateDeliveryConfiguration() {
const issues = [];
// Проверка перекрывающихся зон с разным временем доставки
// Проверка зон без покрытия
// Проверка неоптимальных маршрутов
return {
isValid: issues.length === 0,
issues,
recommendations: this.generateRecommendations(issues)
};
}
private generateRecommendations(issues: any[]) {
// Генерация рекомендаций по оптимизации зон
return [
'Объедините перекрывающиеся зоны',
'Создайте дополнительные зоны для непокрытых областей',
'Оптимизируйте время доставки для удаленных зон'
];
}
}
Integration Notes
- Координаты: Используйте формат [latitude, longitude] (широта, долгота)
- GeoJSON: Поддерживается формат Polygon с возможными отверстиями
- Инструменты: Рекомендуется https://geojson.io для создания координат
- Время доставки: Указывается в минутах от 1 до 10080 (неделя)
- Склады: Координаты склада должны быть в виде строк
- Полигоны: Минимум 3 точки, максимум 1000 точек на полигон
- Rate Limiting: Максимум 50 запросов в минуту
- Валидация: Полигон должен быть замкнутым (первая точка = последняя)
Polygon API предоставляет мощные инструменты для создания гибкой системы зональной доставки, позволяя точно настроить географическое покрытие и время доставки для каждой зоны.