v1
This commit is contained in:
parent
1d90f1d0cc
commit
8b031562ba
173
AGENTS.md
Normal file
173
AGENTS.md
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<!-- vibe42-agents-version: v3-guided-2026-06-01 -->
|
||||||
|
# Vibe42 — учебная песочница для лендингов
|
||||||
|
|
||||||
|
Workspace юзера `Ermek_aubakirov`. Это **учебная среда**, где обычные люди (не разработчики) пробуют сделать свой первый сайт.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 ТВОЯ РОЛЬ
|
||||||
|
|
||||||
|
Ты — **гид и помощник**, а не слепой исполнитель. Цель сессии — чтобы юзер вышел с:
|
||||||
|
1. **рабочим лендингом**, опубликованным по адресу `https://pages.git.vibe42.kz/Ermek_aubakirov/<repo>/`,
|
||||||
|
2. ощущением «это было легко» — без серверов, БД, токенов, конфигов.
|
||||||
|
|
||||||
|
Юзер не разработчик. Ему важен **результат, который видно в браузере**, а не код.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗺 СЦЕНАРИЙ ПЕРВОГО ЗАХОДА (юзер только зашёл, ещё ничего нет)
|
||||||
|
|
||||||
|
1. Поздоровайся коротко: «Привет! Тут за 10 минут собираем лендинг и публикуем его в интернете. О чём хочешь сделать?»
|
||||||
|
2. Если он не знает — предложи **4 конкретных идеи** (выбирай близкие к нему, не абстрактные):
|
||||||
|
- Промо хобби (фотография / музыка / спорт)
|
||||||
|
- Резюме / personal page с контактами
|
||||||
|
- Афиша мероприятия (концерт, день рождения, мастер-класс)
|
||||||
|
- Меню заведения / прайс услуг
|
||||||
|
- Лендинг продукта или будущего проекта (waitlist)
|
||||||
|
3. Уточни **2 короткие детали**: стиль (тёмный/светлый/яркий) и главную цель (рассказать / собрать заявку / показать работы).
|
||||||
|
4. Сразу делай `./new-project <name>` и собирай страницу. Не спрашивай разрешения на каждый шаг.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💬 ЕСЛИ ЮЗЕР ОТВЕЧАЕТ РАСПЛЫВЧАТО
|
||||||
|
|
||||||
|
Юзер говорит «сделай что-нибудь» / «ну хз» / «сюрприз» → **не делай ничего абстрактного**.
|
||||||
|
|
||||||
|
Скажи: «Давай определимся, я задам 3 коротких вопроса:
|
||||||
|
1. Это для тебя лично, для проекта/бизнеса, или для события?
|
||||||
|
2. Главная цель — рассказать о чём-то / собрать заявку / показать портфолио?
|
||||||
|
3. Любимое настроение — строгое тёмное, лёгкое светлое, яркое цветное?»
|
||||||
|
|
||||||
|
После ответов **сразу** предложи 2 конкретных варианта названия+структуры. Дай выбрать и иди делать.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚫 ЕСЛИ ЮЗЕР ХОЧЕТ СЛОЖНОЕ — ПЕРЕФОРМУЛИРУЙ В ЛЕНДИНГ
|
||||||
|
|
||||||
|
| Запрос | Что делаем вместо |
|
||||||
|
|--------|-------------------|
|
||||||
|
| «магазин с корзиной» | лендинг с товарами + кнопка «купить» = ссылка на WhatsApp / Telegram |
|
||||||
|
| «соцсеть» | лендинг будущего проекта + waitlist-форма (Formspree / Getform) |
|
||||||
|
| «блог с админкой» | personal-page + ссылки на статьи в Telegram/Medium |
|
||||||
|
| «приложение для записи» | лендинг услуги + ссылка на Calendly / WhatsApp |
|
||||||
|
| «сайт с входом юзеров» | публичный лендинг без логина (нам логин не нужен) |
|
||||||
|
| «бот в Telegram» | лендинг с описанием бота + кнопка `t.me/...` |
|
||||||
|
|
||||||
|
**Не говори «это невозможно».** Скажи: «У нас песочница только для статических сайтов. Давай сделаем лендинг, который покажет твою идею — а кнопки/формы свяжем с готовыми сервисами (WhatsApp, Telegram, Formspree)». Юзер счастлив, результат за 15 минут.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📐 ШАБЛОНЫ СТРАНИЦ (выбирай под идею юзера)
|
||||||
|
|
||||||
|
### A — Промо продукта/услуги
|
||||||
|
**Секции:** Hero (заголовок + подзаголовок + CTA-кнопка) → 3-4 преимущества (иконка emoji + текст) → социальное доказательство (отзыв или цифра) → CTA (кнопка/телефон/мессенджер).
|
||||||
|
|
||||||
|
### B — Personal / резюме
|
||||||
|
**Секции:** Hero (фото-аватарка + имя + одна фраза «кто я») → О себе (1-2 абзаца) → 3-5 карточек проектов/опыта → Контакты (email, telegram, github как ссылки-кнопки).
|
||||||
|
|
||||||
|
### C — Афиша мероприятия
|
||||||
|
**Секции:** Hero (название + дата + место крупно) → Программа (список с временем) → Локация (картинка-placeholder + адрес) → Регистрация (форма Formspree или контакт).
|
||||||
|
|
||||||
|
### D — Меню / прайс
|
||||||
|
**Секции:** Hero (название + слоган) → Меню/прайс (категории с ценами) → Контакты (телефон, адрес, часы работы, карта-картинка).
|
||||||
|
|
||||||
|
### E — Waitlist для будущего проекта
|
||||||
|
**Секции:** Hero (название проекта + одна фраза + email-форма) → 3 фичи «что будет» → FAQ (3 пункта) → CTA (та же email-форма).
|
||||||
|
|
||||||
|
Все шаблоны — **одна страница, прокрутка вниз**. Никаких роутов, ничего динамического.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚡ РИТУАЛ ПОСЛЕ ПЕРВОГО ЗАПУСКА
|
||||||
|
|
||||||
|
Как только готов первый рабочий вариант (даже грубый):
|
||||||
|
|
||||||
|
1. **Сразу запушь:**
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
git commit -m "v1"
|
||||||
|
git push origin HEAD:pages
|
||||||
|
```
|
||||||
|
2. **ОБЯЗАТЕЛЬНО** дай юзеру ссылку **жирно**:
|
||||||
|
> 🎉 Готово! Твой лендинг здесь: **https://pages.git.vibe42.kz/Ermek_aubakirov/<repo>/**
|
||||||
|
3. Скажи: «Открой в новой вкладке, посмотри. Что хочешь поменять?»
|
||||||
|
4. Дальше короткие итерации: правка → push → новый URL-показ. Каждые 2-3 правки — push.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ ЖЕЛЕЗНЫЕ ПРАВИЛА (НЕ нарушать никогда)
|
||||||
|
|
||||||
|
1. **Только статика — HTML + CSS + JS в браузере.**
|
||||||
|
2. **Никакого бэкенда.** Никаких Node/Express/FastAPI/Django/PHP/Go-серверов. Никаких БД. Никакого Redis.
|
||||||
|
3. **Никакой аутентификации / OAuth / JWT.**
|
||||||
|
4. **Никакого Docker, nginx, sudo, системных настроек.**
|
||||||
|
5. **Никаких тяжёлых сборщиков** (`npm install` дерево на 500МБ). Tailwind — только через CDN.
|
||||||
|
6. **НИКОГДА `git init` в workspace root (`/workspaces/Ermek_aubakirov`)** — это папка-контейнер юзера, не репозиторий.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ ВСЕГДА работай через `./new-project`
|
||||||
|
|
||||||
|
Если юзер сказал «сделай сайт NAME» / «создай проект NAME»:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspaces/Ermek_aubakirov
|
||||||
|
./new-project NAME # создаёт repo в Gitea + клонит локально в ./NAME/
|
||||||
|
cd NAME
|
||||||
|
# теперь создавай index.html / style.css / script.js внутри ./NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
`./new-project` сам создаёт repo, клонит, и копирует туда `AGENTS.md` + `design.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌐 Git и публикация
|
||||||
|
|
||||||
|
**НЕТ GitHub.** Self-hosted git: **https://git.vibe42.kz**
|
||||||
|
|
||||||
|
- Профиль юзера: https://git.vibe42.kz/Ermek_aubakirov
|
||||||
|
- Pages (живые лендинги): https://pages.git.vibe42.kz/Ermek_aubakirov/<repo>/
|
||||||
|
- Креды уже в `/workspaces/Ermek_aubakirov/.git-credentials` — git push/clone работают без пароля
|
||||||
|
- **НЕ спрашивай юзера про GitHub URL / токен** — их не нужно
|
||||||
|
|
||||||
|
### Опубликовать лендинг
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
git commit -m "site"
|
||||||
|
git push origin HEAD:pages
|
||||||
|
```
|
||||||
|
|
||||||
|
Ветка **`pages`** (Caddy её обслуживает; `gh-pages` тоже работает как fallback). Push → лендинг доступен мгновенно.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Когда что-то идёт не так
|
||||||
|
|
||||||
|
- **Pages 404** → запушь ветку `pages` снова: `git push origin HEAD:pages -f`
|
||||||
|
- **Не дёргай Gitea API типа `/repos/.../pages`, `/settings/pages`, `/deploy_keys`** — их нет
|
||||||
|
- **Не пытайся «настроить Pages через UI Gitea»** — Pages у нас работают только через push в ветку `pages`
|
||||||
|
- Запуталось — сделай новый чистый проект через `./new-project NAME-v2`, перенеси туда работающий index.html
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❌ Чего НЕ делать НИКОГДА
|
||||||
|
|
||||||
|
- ❌ `git init` в workspace root
|
||||||
|
- ❌ `npm install` с прод-зависимостями (express/mongoose/pg/prisma/next/nuxt)
|
||||||
|
- ❌ Создавать `server.js` / `app.py` / `main.go` как backend
|
||||||
|
- ❌ Использовать `gh` CLI или GitHub API
|
||||||
|
- ❌ Вызывать Gitea Pages-API (его нет)
|
||||||
|
- ❌ Долгое отлаживание Pages — почти всегда решение «push HEAD:pages»
|
||||||
|
- ❌ Просить юзера ввести токен/URL/пароль — всё уже настроено
|
||||||
|
- ❌ Задавать юзеру 10 вопросов подряд (максимум 2-3 за раз)
|
||||||
|
- ❌ Показывать юзеру голый код больше 1 раза — ему важен результат, а не как написано
|
||||||
|
- ❌ Предлагать «давай сначала дизайн в Figma» — мы делаем сразу в HTML
|
||||||
|
- ❌ Говорить «это сложно» — переформулируй в простое
|
||||||
|
- ❌ Зависать в обсуждениях — сделай первый вариант грубо, потом итерируй
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 design.md
|
||||||
|
|
||||||
|
Рядом лежит `design.md` с готовой палитрой, типографикой и стартер-шаблоном `index.html`. **Начинай с него.** Не выдумывай новые цвета — модифицируй существующие.
|
||||||
110
design.md
Normal file
110
design.md
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<!-- vibe42-design-version: v1-2026-06-01 -->
|
||||||
|
# Design system — Vibe42 песочница
|
||||||
|
|
||||||
|
Базовые цвета и типографика для лендингов. Можно отклоняться, но начинай с этого.
|
||||||
|
|
||||||
|
## Палитра
|
||||||
|
|
||||||
|
| Token | Hex | Использование |
|
||||||
|
|-------|-----|---------------|
|
||||||
|
| `--ink` | `#0F1218` | Тёмный фон / основной текст |
|
||||||
|
| `--cyan` | `#00E5FF` | Основной акцент (кнопки, лого) |
|
||||||
|
| `--cyan-50` | `#E8FCFF` | Светлая подложка для акцентов |
|
||||||
|
| `--white` | `#FFFFFF` | Основной фон |
|
||||||
|
| `--gray-500` | `#5B6573` | Вторичный текст |
|
||||||
|
| `--gray-100` | `#F2F4F7` | Сепараторы / тонкие фоны |
|
||||||
|
|
||||||
|
## Типографика
|
||||||
|
|
||||||
|
```css
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
|
||||||
|
```
|
||||||
|
|
||||||
|
| Уровень | Размер | Вес | line-height |
|
||||||
|
|---------|--------|-----|-------------|
|
||||||
|
| h1 (hero) | 56px | 800 | 1.05 |
|
||||||
|
| h2 (section) | 36px | 700 | 1.15 |
|
||||||
|
| h3 | 22px | 700 | 1.3 |
|
||||||
|
| body | 17px | 400 | 1.6 |
|
||||||
|
| small | 14px | 400 | 1.5 |
|
||||||
|
|
||||||
|
На мобиле — h1 уменьши до 36px, h2 до 28px.
|
||||||
|
|
||||||
|
## Лейаут
|
||||||
|
|
||||||
|
- max-width контента: **1140px** (контейнер с padding по бокам)
|
||||||
|
- секция: `padding: 80px 24px` (мобила: `48px 20px`)
|
||||||
|
- gap между блоками внутри секции: `24-32px`
|
||||||
|
- border-radius: `8px` (кнопки, карточки), `16px` (большие карточки)
|
||||||
|
|
||||||
|
## Кнопки
|
||||||
|
|
||||||
|
```css
|
||||||
|
.btn-primary {
|
||||||
|
background: var(--cyan); color: var(--ink);
|
||||||
|
padding: 14px 28px; border-radius: 8px;
|
||||||
|
font-weight: 700; text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.btn-secondary {
|
||||||
|
background: transparent; color: var(--ink);
|
||||||
|
border: 2px solid var(--ink);
|
||||||
|
padding: 12px 26px; border-radius: 8px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Стартер `index.html`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>Мой проект</title>
|
||||||
|
<style>
|
||||||
|
:root{--ink:#0F1218;--cyan:#00E5FF;--cyan-50:#E8FCFF;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7}
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
body{font:17px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--white)}
|
||||||
|
.container{max-width:1140px;margin:0 auto;padding:80px 24px}
|
||||||
|
.hero{background:var(--ink);color:var(--white)}
|
||||||
|
.hero h1{font-size:56px;font-weight:800;line-height:1.05;margin-bottom:24px}
|
||||||
|
.hero p{font-size:20px;color:#9aa3b2;max-width:600px;margin-bottom:32px}
|
||||||
|
.btn{display:inline-block;background:var(--cyan);color:var(--ink);padding:14px 28px;border-radius:8px;font-weight:700;text-decoration:none}
|
||||||
|
.btn:hover{background:#1be5ff}
|
||||||
|
.section h2{font-size:36px;font-weight:700;margin-bottom:24px}
|
||||||
|
.card{background:var(--gray-100);border-radius:16px;padding:32px;margin-bottom:16px}
|
||||||
|
@media (max-width:640px){.hero h1{font-size:36px}.section h2{font-size:28px}.container{padding:48px 20px}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section class="hero">
|
||||||
|
<div class="container">
|
||||||
|
<h1>Заголовок проекта</h1>
|
||||||
|
<p>Подзаголовок — пара предложений о чём это.</p>
|
||||||
|
<a class="btn" href="#section">Начать</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section id="section" class="section">
|
||||||
|
<div class="container">
|
||||||
|
<h2>Секция</h2>
|
||||||
|
<div class="card">Контент карточки.</div>
|
||||||
|
<div class="card">Контент карточки.</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Чем НЕ пользоваться
|
||||||
|
|
||||||
|
- Bootstrap, Material UI, Chakra, Ant Design — слишком тяжело и не нужно для лендинга
|
||||||
|
- Font Awesome — используй emoji (🚀 ⚡ ✨) или inline SVG
|
||||||
|
- jQuery — vanilla JS более чем достаточно
|
||||||
|
|
||||||
|
## Чем МОЖНО (если очень надо)
|
||||||
|
|
||||||
|
- **Tailwind через CDN**: `<script src="https://cdn.tailwindcss.com"></script>` — для прототипа OK
|
||||||
|
- **Lottie animations через CDN**
|
||||||
|
- **Placeholder картинки**: `https://picsum.photos/800/600`, `https://placehold.co/600x400`
|
||||||
|
- **Шрифты Google Fonts через `<link>`** в head
|
||||||
126
index.html
Normal file
126
index.html
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>PoleMap — расстановка опор</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<style>
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;background:#f1f5f9;color:#0f172a}
|
||||||
|
#map{height:100%;width:100%;border-radius:12px;min-height:500px}
|
||||||
|
.pole-badge{display:inline-block;padding:2px 10px;border-radius:20px;color:#fff;font-size:12px;font-weight:600}
|
||||||
|
.pole-type-btn{padding:8px 14px;border-radius:8px;border:2px solid #e2e8f0;cursor:pointer;font-size:13px;font-weight:500;transition:all .15s;background:#fff}
|
||||||
|
.pole-type-btn:hover{border-color:#94a3b8}
|
||||||
|
.pole-type-btn.active{border-color:#0f172a;background:#0f172a;color:#fff}
|
||||||
|
.pole-type-btn .dot{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:6px;vertical-align:middle}
|
||||||
|
#searchResults{position:absolute;top:100%;left:0;right:0;z-index:1000;background:#fff;border:1px solid #e2e8f0;border-radius:8px;max-height:240px;overflow-y:auto;box-shadow:0 4px 12px rgba(0,0,0,.1)}
|
||||||
|
#searchResults li{font-size:14px;padding:8px 14px}
|
||||||
|
#searchResults li:hover{background:#f0f9ff}
|
||||||
|
.btn-primary{background:#0f172a;color:#fff;padding:10px 24px;border-radius:8px;font-weight:600;border:none;cursor:pointer;transition:all .15s;font-size:14px}
|
||||||
|
.btn-primary:hover{background:#1e293b}
|
||||||
|
.btn-secondary{background:transparent;color:#0f172a;padding:9px 22px;border-radius:8px;font-weight:500;border:2px solid #cbd5e1;cursor:pointer;transition:all .15s;font-size:14px}
|
||||||
|
.btn-secondary:hover{border-color:#94a3b8}
|
||||||
|
input,select{width:100%;padding:10px 14px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px;background:#fff;outline:none}
|
||||||
|
input:focus,select:focus{border-color:#0f172a}
|
||||||
|
table{width:100%;border-collapse:collapse;font-size:14px}
|
||||||
|
table th{text-align:left;padding:10px 16px;font-size:12px;text-transform:uppercase;letter-spacing:.05em;color:#64748b;border-bottom:2px solid #e2e8f0}
|
||||||
|
table td{padding:10px 16px;border-bottom:1px solid #f1f5f9}
|
||||||
|
.scrollbar-thin::-webkit-scrollbar{width:6px}
|
||||||
|
.scrollbar-thin::-webkit-scrollbar-track{background:transparent}
|
||||||
|
.scrollbar-thin::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:3px}
|
||||||
|
.leaflet-popup-content{font-size:13px;line-height:1.5}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="flex h-screen overflow-hidden">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<div class="w-[420px] min-w-[420px] bg-white border-r border-gray-200 flex flex-col overflow-hidden">
|
||||||
|
<div class="px-5 py-4 border-b border-gray-200">
|
||||||
|
<h1 class="text-xl font-bold">PoleMap</h1>
|
||||||
|
<p class="text-sm text-gray-500 mt-1">Расстановка опор связи на карте</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex-1 overflow-y-auto scrollbar-thin px-5 py-4 space-y-5">
|
||||||
|
<!-- Search -->
|
||||||
|
<div class="relative">
|
||||||
|
<label class="text-xs font-semibold uppercase tracking-wider text-gray-500 mb-1.5 block">Поиск села</label>
|
||||||
|
<input id="searchInput" type="text" placeholder="Начните вводить название..." autocomplete="off">
|
||||||
|
<ul id="searchResults"></ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Selectors -->
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div>
|
||||||
|
<label class="text-xs font-semibold uppercase tracking-wider text-gray-500 mb-1.5 block">Область</label>
|
||||||
|
<select id="oblast"><option value="">— Выберите область —</option></select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="text-xs font-semibold uppercase tracking-wider text-gray-500 mb-1.5 block">Район</label>
|
||||||
|
<select id="rayon"><option value="">— Выберите район —</option></select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="text-xs font-semibold uppercase tracking-wider text-gray-500 mb-1.5 block">Село</label>
|
||||||
|
<select id="village"><option value="">— Выберите село —</option></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pole types -->
|
||||||
|
<div>
|
||||||
|
<label class="text-xs font-semibold uppercase tracking-wider text-gray-500 mb-2 block">Тип опоры</label>
|
||||||
|
<div class="grid grid-cols-2 gap-2">
|
||||||
|
<button class="pole-type-btn" data-type="1"><span class="dot" style="background:#2563eb"></span>Промежуточная</button>
|
||||||
|
<button class="pole-type-btn" data-type="2"><span class="dot" style="background:#dc2626"></span>Угловая</button>
|
||||||
|
<button class="pole-type-btn" data-type="3"><span class="dot" style="background:#ca8a04"></span>С муфтой</button>
|
||||||
|
<button class="pole-type-btn" data-type="4"><span class="dot" style="background:#16a34a"></span>С ОРКсп</button>
|
||||||
|
<button class="pole-type-btn" data-type="5"><span class="dot" style="background:#7c3aed"></span>С муфтой и ОРКсп</button>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-gray-400 mt-2">Кликните на карту, чтобы поставить опору выбранного типа</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Actions -->
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button class="btn-primary flex-1" onclick="exportSVG()">📥 SVG</button>
|
||||||
|
<button class="btn-secondary flex-1" onclick="exportVSD()">📥 VSD</button>
|
||||||
|
<button class="btn-secondary px-3" onclick="clearAll()" title="Очистить всё">🗑</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Map + Table -->
|
||||||
|
<div class="flex-1 flex flex-col overflow-hidden">
|
||||||
|
<div class="flex-1 p-3">
|
||||||
|
<div id="map"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="h-64 bg-white border-t border-gray-200 overflow-y-auto scrollbar-thin">
|
||||||
|
<div class="px-5 py-3 flex items-center justify-between border-b border-gray-100">
|
||||||
|
<span class="font-semibold text-sm">Опоры (<span id="poleCount">0</span>)</span>
|
||||||
|
<span class="text-xs text-gray-400">Нумерация по порядку установки</span>
|
||||||
|
</div>
|
||||||
|
<table id="poleTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-14">№</th>
|
||||||
|
<th>Тип</th>
|
||||||
|
<th>Широта</th>
|
||||||
|
<th>Долгота</th>
|
||||||
|
<th class="text-right">Дист. до пред.</th>
|
||||||
|
<th class="w-10"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td colspan="6" class="text-center text-gray-500 py-4">Кликните на карту, чтобы добавить опору</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||||
|
<script src="js/kz-data.js"></script>
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
379
js/app.js
Normal file
379
js/app.js
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
let map, drawnItems, poleLayer, connectionLayer;
|
||||||
|
let poles = [];
|
||||||
|
let poleCounter = 1;
|
||||||
|
let currentPoleType = "1";
|
||||||
|
|
||||||
|
const POLE_TYPES = {
|
||||||
|
"1": { name: "Промежуточная", color: "#2563eb", icon: "●" },
|
||||||
|
"2": { name: "Угловая", color: "#dc2626", icon: "◆" },
|
||||||
|
"3": { name: "С муфтой", color: "#ca8a04", icon: "◎" },
|
||||||
|
"4": { name: "С ОРКсп", color: "#16a34a", icon: "◇" },
|
||||||
|
"5": { name: "С муфтой и ОРКсп", color: "#7c3aed", icon: "★" },
|
||||||
|
};
|
||||||
|
|
||||||
|
function initApp() {
|
||||||
|
initMap();
|
||||||
|
initSelectors();
|
||||||
|
initPoleControls();
|
||||||
|
updateTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initMap() {
|
||||||
|
map = L.map("map", { zoomControl: true }).setView([48.0, 68.0], 6);
|
||||||
|
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||||
|
attribution: "© OpenStreetMap",
|
||||||
|
maxZoom: 19,
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
drawnItems = L.featureGroup().addTo(map);
|
||||||
|
poleLayer = L.featureGroup().addTo(map);
|
||||||
|
connectionLayer = L.featureGroup().addTo(map);
|
||||||
|
|
||||||
|
map.on("click", function (e) {
|
||||||
|
addPole(e.latlng.lat, e.latlng.lng);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSelectors() {
|
||||||
|
const oblastSel = document.getElementById("oblast");
|
||||||
|
const rayonSel = document.getElementById("rayon");
|
||||||
|
const villageSel = document.getElementById("village");
|
||||||
|
const searchInput = document.getElementById("searchInput");
|
||||||
|
|
||||||
|
KZ_DATA.forEach((o) => {
|
||||||
|
const opt = document.createElement("option");
|
||||||
|
opt.value = o.oblast;
|
||||||
|
opt.textContent = o.oblast;
|
||||||
|
oblastSel.appendChild(opt);
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateRayons() {
|
||||||
|
rayonSel.innerHTML = '<option value="">— Выберите район —</option>';
|
||||||
|
villageSel.innerHTML = '<option value="">— Выберите село —</option>';
|
||||||
|
const oblast = KZ_DATA.find((o) => o.oblast === oblastSel.value);
|
||||||
|
if (!oblast) return;
|
||||||
|
oblast.rayons.forEach((r) => {
|
||||||
|
const opt = document.createElement("option");
|
||||||
|
opt.value = r.rayon;
|
||||||
|
opt.textContent = r.rayon;
|
||||||
|
rayonSel.appendChild(opt);
|
||||||
|
});
|
||||||
|
updateVillages();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateVillages() {
|
||||||
|
villageSel.innerHTML = '<option value="">— Выберите село —</option>';
|
||||||
|
const oblast = KZ_DATA.find((o) => o.oblast === oblastSel.value);
|
||||||
|
if (!oblast) return;
|
||||||
|
const rayon = oblast.rayons.find((r) => r.rayon === rayonSel.value);
|
||||||
|
if (!rayon) return;
|
||||||
|
rayon.villages.forEach((v) => {
|
||||||
|
const opt = document.createElement("option");
|
||||||
|
opt.value = v;
|
||||||
|
opt.textContent = v;
|
||||||
|
villageSel.appendChild(opt);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
oblastSel.addEventListener("change", updateRayons);
|
||||||
|
rayonSel.addEventListener("change", updateVillages);
|
||||||
|
|
||||||
|
villageSel.addEventListener("change", function () {
|
||||||
|
const name = this.value;
|
||||||
|
if (!name) return;
|
||||||
|
const oblast = KZ_DATA.find((o) => o.oblast === oblastSel.value);
|
||||||
|
if (!oblast) return;
|
||||||
|
map.setView(oblast.center, 10);
|
||||||
|
L.popup()
|
||||||
|
.setLatLng(oblast.center)
|
||||||
|
.setContent(`<b>${name}</b><br>${oblast.oblast}, ${rayonSel.value}`)
|
||||||
|
.openOn(map);
|
||||||
|
});
|
||||||
|
|
||||||
|
searchInput.addEventListener("input", function () {
|
||||||
|
const q = this.value.toLowerCase().trim();
|
||||||
|
if (q.length < 2) return;
|
||||||
|
const results = [];
|
||||||
|
KZ_DATA.forEach((o) => {
|
||||||
|
o.rayons.forEach((r) => {
|
||||||
|
r.villages.forEach((v) => {
|
||||||
|
if (v.toLowerCase().includes(q)) {
|
||||||
|
results.push({ village: v, rayon: r.rayon, oblast: o.oblast, center: o.center });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const list = document.getElementById("searchResults");
|
||||||
|
list.innerHTML = "";
|
||||||
|
if (results.length === 0 || results.length > 50) {
|
||||||
|
if (results.length > 50) list.innerHTML = "<li class='px-3 py-2 text-gray-500'>Слишком много результатов, уточните</li>";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
results.slice(0, 20).forEach((r) => {
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.className = "px-3 py-2 cursor-pointer hover:bg-cyan-50";
|
||||||
|
li.textContent = `${r.village} — ${r.rayon}, ${r.oblast}`;
|
||||||
|
li.addEventListener("click", () => {
|
||||||
|
oblastSel.value = r.oblast;
|
||||||
|
updateRayons();
|
||||||
|
rayonSel.value = r.rayon;
|
||||||
|
updateVillages();
|
||||||
|
villageSel.value = r.village;
|
||||||
|
map.setView(r.center, 12);
|
||||||
|
L.popup()
|
||||||
|
.setLatLng(r.center)
|
||||||
|
.setContent(`<b>${r.village}</b><br>${r.oblast}, ${r.rayon}`)
|
||||||
|
.openOn(map);
|
||||||
|
list.innerHTML = "";
|
||||||
|
searchInput.value = r.village;
|
||||||
|
});
|
||||||
|
list.appendChild(li);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("click", function (e) {
|
||||||
|
if (!e.target.closest("#searchResults") && !e.target.closest("#searchInput")) {
|
||||||
|
document.getElementById("searchResults").innerHTML = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initPoleControls() {
|
||||||
|
document.querySelectorAll(".pole-type-btn").forEach((btn) => {
|
||||||
|
btn.addEventListener("click", function () {
|
||||||
|
document.querySelectorAll(".pole-type-btn").forEach((b) => b.classList.remove("active"));
|
||||||
|
this.classList.add("active");
|
||||||
|
currentPoleType = this.dataset.type;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
document.querySelector('[data-type="1"]').classList.add("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPole(lat, lng) {
|
||||||
|
const type = POLE_TYPES[currentPoleType];
|
||||||
|
const id = poleCounter++;
|
||||||
|
const pole = { id, lat, lng, type: currentPoleType, label: `Опора ${id}` };
|
||||||
|
poles.push(pole);
|
||||||
|
renderPole(pole);
|
||||||
|
updateTable();
|
||||||
|
updateConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPole(pole) {
|
||||||
|
const type = POLE_TYPES[pole.type];
|
||||||
|
const icon = L.divIcon({
|
||||||
|
className: "pole-marker",
|
||||||
|
html: `<div style="
|
||||||
|
width:28px;height:28px;border-radius:50%;
|
||||||
|
background:${type.color};color:#fff;
|
||||||
|
display:flex;align-items:center;justify-content:center;
|
||||||
|
font-size:14px;font-weight:700;border:2px solid #fff;
|
||||||
|
box-shadow:0 2px 6px rgba(0,0,0,.3);
|
||||||
|
cursor:pointer;
|
||||||
|
" title="${pole.label} (${type.name})">${pole.id}</div>`,
|
||||||
|
iconSize: [28, 28],
|
||||||
|
iconAnchor: [14, 14],
|
||||||
|
});
|
||||||
|
|
||||||
|
const marker = L.marker([pole.lat, pole.lng], { icon }).addTo(poleLayer);
|
||||||
|
marker.bindPopup(`
|
||||||
|
<b>${pole.label}</b><br>
|
||||||
|
Тип: ${type.name}<br>
|
||||||
|
Координаты: ${pole.lat.toFixed(6)}, ${pole.lng.toFixed(6)}
|
||||||
|
<br><button onclick="removePole(${pole.id})" style="color:red;cursor:pointer;border:none;background:none;margin-top:4px">✕ Удалить</button>
|
||||||
|
`);
|
||||||
|
pole._marker = marker;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePole(id) {
|
||||||
|
const idx = poles.findIndex((p) => p.id === id);
|
||||||
|
if (idx === -1) return;
|
||||||
|
const pole = poles[idx];
|
||||||
|
if (pole._marker) poleLayer.removeLayer(pole._marker);
|
||||||
|
poles.splice(idx, 1);
|
||||||
|
poles.forEach((p, i) => {
|
||||||
|
p.id = i + 1;
|
||||||
|
p.label = `Опора ${i + 1}`;
|
||||||
|
});
|
||||||
|
poleCounter = poles.length + 1;
|
||||||
|
poleLayer.clearLayers();
|
||||||
|
poles.forEach((p) => renderPole(p));
|
||||||
|
updateTable();
|
||||||
|
updateConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateConnections() {
|
||||||
|
connectionLayer.clearLayers();
|
||||||
|
if (poles.length < 2) return;
|
||||||
|
|
||||||
|
const sorted = [...poles].sort((a, b) => a.id - b.id);
|
||||||
|
for (let i = 0; i < sorted.length - 1; i++) {
|
||||||
|
const a = sorted[i];
|
||||||
|
const b = sorted[i + 1];
|
||||||
|
const polyline = L.polyline(
|
||||||
|
[
|
||||||
|
[a.lat, a.lng],
|
||||||
|
[b.lat, b.lng],
|
||||||
|
],
|
||||||
|
{ color: "#64748b", weight: 2, dashArray: "6,4", opacity: 0.7 }
|
||||||
|
).addTo(connectionLayer);
|
||||||
|
|
||||||
|
const midLat = (a.lat + b.lat) / 2;
|
||||||
|
const midLng = (a.lng + b.lng) / 2;
|
||||||
|
const dist = calcDistance(a.lat, a.lng, b.lat, b.lng);
|
||||||
|
L.marker([midLat, midLng], {
|
||||||
|
icon: L.divIcon({
|
||||||
|
className: "dist-label",
|
||||||
|
html: `<div style="
|
||||||
|
background:rgba(255,255,255,.9);padding:2px 8px;
|
||||||
|
border-radius:4px;font-size:12px;font-weight:600;
|
||||||
|
border:1px solid #ccc;white-space:nowrap;
|
||||||
|
">${dist.toFixed(1)} м</div>`,
|
||||||
|
iconSize: [0, 0],
|
||||||
|
iconAnchor: [0, 0],
|
||||||
|
}),
|
||||||
|
}).addTo(connectionLayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTable() {
|
||||||
|
document.getElementById("poleCount").textContent = poles.length;
|
||||||
|
const tbody = document.querySelector("#poleTable tbody");
|
||||||
|
tbody.innerHTML = "";
|
||||||
|
if (poles.length === 0) {
|
||||||
|
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-gray-500 py-4">Нет опор. Кликните на карту, чтобы добавить.</td></tr>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorted = [...poles].sort((a, b) => a.id - b.id);
|
||||||
|
sorted.forEach((p, i) => {
|
||||||
|
const type = POLE_TYPES[p.type];
|
||||||
|
const prevDist =
|
||||||
|
i > 0
|
||||||
|
? calcDistance(sorted[i - 1].lat, sorted[i - 1].lng, p.lat, p.lng).toFixed(1)
|
||||||
|
: "—";
|
||||||
|
const tr = document.createElement("tr");
|
||||||
|
tr.className = "border-b border-gray-200 hover:bg-gray-50";
|
||||||
|
tr.innerHTML = `
|
||||||
|
<td class="px-4 py-2 font-medium">${p.id}</td>
|
||||||
|
<td class="px-4 py-2"><span class="pole-badge" style="background:${type.color}">${type.name}</span></td>
|
||||||
|
<td class="px-4 py-2 font-mono text-sm">${p.lat.toFixed(6)}</td>
|
||||||
|
<td class="px-4 py-2 font-mono text-sm">${p.lng.toFixed(6)}</td>
|
||||||
|
<td class="px-4 py-2 text-right font-mono">${prevDist}</td>
|
||||||
|
<td class="px-4 py-2 text-center">
|
||||||
|
<button onclick="removePole(${p.id})" class="text-red-500 hover:text-red-700 text-sm" title="Удалить">✕</button>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
tbody.appendChild(tr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcDistance(lat1, lon1, lat2, lon2) {
|
||||||
|
const R = 6371000;
|
||||||
|
const dLat = ((lat2 - lat1) * Math.PI) / 180;
|
||||||
|
const dLon = ((lon2 - lon1) * Math.PI) / 180;
|
||||||
|
const a =
|
||||||
|
Math.sin(dLat / 2) ** 2 +
|
||||||
|
Math.cos((lat1 * Math.PI) / 180) *
|
||||||
|
Math.cos((lat2 * Math.PI) / 180) *
|
||||||
|
Math.sin(dLon / 2) ** 2;
|
||||||
|
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAll() {
|
||||||
|
if (poles.length === 0) return;
|
||||||
|
if (!confirm("Удалить все опоры?")) return;
|
||||||
|
poleLayer.clearLayers();
|
||||||
|
connectionLayer.clearLayers();
|
||||||
|
poles = [];
|
||||||
|
poleCounter = 1;
|
||||||
|
updateTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportSVG() {
|
||||||
|
if (poles.length === 0) return alert("Нет опор для экспорта");
|
||||||
|
|
||||||
|
const padding = 50;
|
||||||
|
const lats = poles.map((p) => p.lat);
|
||||||
|
const lngs = poles.map((p) => p.lng);
|
||||||
|
const minLat = Math.min(...lats);
|
||||||
|
const maxLat = Math.max(...lats);
|
||||||
|
const minLng = Math.min(...lngs);
|
||||||
|
const maxLng = Math.max(...lngs);
|
||||||
|
const latRange = maxLat - minLat || 0.001;
|
||||||
|
const lngRange = maxLng - minLng || 0.001;
|
||||||
|
|
||||||
|
const scale = 500 / Math.max(latRange, lngRange);
|
||||||
|
const toX = (lng) => padding + ((lng - minLng) / lngRange) * 500;
|
||||||
|
const toY = (lat) => padding + ((maxLat - lat) / latRange) * 500;
|
||||||
|
const w = 500 + padding * 2;
|
||||||
|
const h = 500 + padding * 2;
|
||||||
|
|
||||||
|
let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${w} ${h}" width="${w}" height="${h}">
|
||||||
|
<style>
|
||||||
|
.pole-label{font:bold 10px sans-serif;text-anchor:middle;fill:#fff}
|
||||||
|
.dist-label{font:9px sans-serif;text-anchor:middle;fill:#333}
|
||||||
|
.title{font:14px sans-serif;font-weight:700;fill:#333}
|
||||||
|
</style>
|
||||||
|
<rect width="100%" height="100%" fill="#f8f9fa"/>
|
||||||
|
<text x="${w / 2}" y="24" text-anchor="middle" class="title">Схема расположения опор</text>`;
|
||||||
|
|
||||||
|
const sorted = [...poles].sort((a, b) => a.id - b.id);
|
||||||
|
|
||||||
|
// connections
|
||||||
|
for (let i = 0; i < sorted.length - 1; i++) {
|
||||||
|
const a = sorted[i],
|
||||||
|
b = sorted[i + 1];
|
||||||
|
const dist = calcDistance(a.lat, a.lng, b.lat, b.lng).toFixed(1);
|
||||||
|
const mx = (toX(a.lng) + toX(b.lng)) / 2;
|
||||||
|
const my = (toY(a.lat) + toY(b.lat)) / 2;
|
||||||
|
svg += `<line x1="${toX(a.lng)}" y1="${toY(a.lat)}" x2="${toX(b.lng)}" y2="${toY(b.lat)}" stroke="#94a3b8" stroke-width="1.5" stroke-dasharray="5,3"/>`;
|
||||||
|
svg += `<text x="${mx}" y="${my - 6}" class="dist-label">${dist} м</text>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// poles
|
||||||
|
sorted.forEach((p) => {
|
||||||
|
const type = POLE_TYPES[p.type];
|
||||||
|
const cx = toX(p.lng);
|
||||||
|
const cy = toY(p.lat);
|
||||||
|
svg += `<circle cx="${cx}" cy="${cy}" r="12" fill="${type.color}" stroke="#fff" stroke-width="2"/>`;
|
||||||
|
svg += `<text x="${cx}" y="${cy + 3}" class="pole-label">${p.id}</text>`;
|
||||||
|
svg += `<text x="${cx + 18}" y="${cy + 3}" font-size="8" fill="#666" font-family="sans-serif">${p.label} (${type.name})</text>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Legend
|
||||||
|
const legendY = h - 40;
|
||||||
|
svg += `<rect x="${padding}" y="${legendY - 10}" width="${w - padding * 2}" height="2" fill="#e2e8f0"/>`;
|
||||||
|
let lx = padding;
|
||||||
|
Object.entries(POLE_TYPES).forEach(([k, t]) => {
|
||||||
|
svg += `<circle cx="${lx + 6}" cy="${legendY + 10}" r="6" fill="${t.color}" stroke="#ccc" stroke-width="1"/>`;
|
||||||
|
svg += `<text x="${lx + 16}" y="${legendY + 14}" font-size="9" fill="#555" font-family="sans-serif">${t.name}</text>`;
|
||||||
|
lx += 140;
|
||||||
|
});
|
||||||
|
|
||||||
|
svg += `</svg>`;
|
||||||
|
|
||||||
|
downloadFile(svg, "opory.svg", "image/svg+xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportVSD() {
|
||||||
|
alert(
|
||||||
|
"VSD (Visio) — бинарный формат. SVG можно открыть в Visio.\n\n" +
|
||||||
|
"Экспортируйте в SVG, затем откройте в Visio: Файл → Открыть → выберите .svg\n" +
|
||||||
|
"Или перетащите SVG-файл в окно Visio."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadFile(content, filename, mime) {
|
||||||
|
const blob = new Blob([content], { type: mime });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", initApp);
|
||||||
294
js/kz-data.js
Normal file
294
js/kz-data.js
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
const KZ_DATA = [
|
||||||
|
{
|
||||||
|
oblast: "Акмолинская", center: [52.25, 69.35],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Целиноградский", villages: ["Косшы", "Кабанбай батыра", "Тайтобе", "Акмол"] },
|
||||||
|
{ rayon: "Аккольский", villages: ["Акколь", "Енбек", "Жалгызкарагай"] },
|
||||||
|
{ rayon: "Аршалынский", villages: ["Аршалы", "Ижевское", "Булаксай"] },
|
||||||
|
{ rayon: "Астраханский", villages: ["Астраханка", "Жалтыр", "Новочеркасское"] },
|
||||||
|
{ rayon: "Атбасарский", villages: ["Атбасар", "Сочинское", "Борисовка"] },
|
||||||
|
{ rayon: "Буландынский", villages: ["Макинск", "Пушкино", "Айнаколь"] },
|
||||||
|
{ rayon: "Бурабайский", villages: ["Щучинск", "Бурабай", "Зеленый Бор"] },
|
||||||
|
{ rayon: "Егиндыкольский", villages: ["Егиндыколь", "Абай", "Коркыт"] },
|
||||||
|
{ rayon: "Ерейментауский", villages: ["Ерейментау", "Ынтымак", "Торгай"] },
|
||||||
|
{ rayon: "Есильский", villages: ["Есиль", "Каратал", "Бузулук"] },
|
||||||
|
{ rayon: "Жаксынский", villages: ["Жаксы", "Беловодское", "Кайракты"] },
|
||||||
|
{ rayon: "Жаркаинский", villages: ["Державинск", "Пригородное", "Тасоткель"] },
|
||||||
|
{ rayon: "Зерендинский", villages: ["Зеренда", "Викторовка", "Кызылагаш"] },
|
||||||
|
{ rayon: "Коргалжынский", villages: ["Коргалжын", "Талдыколь", "Майшукыр"] },
|
||||||
|
{ rayon: "Сандыктауский", villages: ["Балкашино", "Максимовка", "Сандыктау"] },
|
||||||
|
{ rayon: "Биржан сал", villages: ["Степняк", "Кенесары", "Андыкожа батыра"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Актюбинская", center: [50.28, 57.21],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Актобе", villages: ["Актобе", "Киргизское", "Курайли"] },
|
||||||
|
{ rayon: "Алгинский", villages: ["Алга", "Бескоспа", "Тамды"] },
|
||||||
|
{ rayon: "Айтекебийский", villages: ["Темирбека Жургенова", "Аккудык", "Торткудык"] },
|
||||||
|
{ rayon: "Байганинский", villages: ["Карауылкельды", "Кемерши", "Кызылбулак"] },
|
||||||
|
{ rayon: "Иргизский", villages: ["Иргиз", "Аманколь", "Каракудык"] },
|
||||||
|
{ rayon: "Каргалинский", villages: ["Богетсай", "Алимбет", "Косестек"] },
|
||||||
|
{ rayon: "Мартукский", villages: ["Мартук", "Каратаусай", "Кызылжар"] },
|
||||||
|
{ rayon: "Мугалжарский", villages: ["Кандыагаш", "Енбек", "Жагабулак"] },
|
||||||
|
{ rayon: "Темирский", villages: ["Шубаркудык", "Шубарши", "Кенкияк"] },
|
||||||
|
{ rayon: "Уилский", villages: ["Уил", "Карасу", "Сандыколь"] },
|
||||||
|
{ rayon: "Хобдинский", villages: ["Кобда", "Кызылжар", "Байтарак"] },
|
||||||
|
{ rayon: "Хромтауский", villages: ["Хромтау", "Абай", "Копта"] },
|
||||||
|
{ rayon: "Шалкарский", villages: ["Шалкар", "Биршогыр", "Тогыз"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Алматинская", center: [44.85, 76.95],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Конаев", villages: ["Конаев", "Шенгельды", "Коккайнар"] },
|
||||||
|
{ rayon: "Балхашский", villages: ["Баканас", "Акжиде", "Караой"] },
|
||||||
|
{ rayon: "Енбекшиказахский", villages: ["Есик", "Тургень", "Маловодное"] },
|
||||||
|
{ rayon: "Жамбылский", villages: ["Узынагаш", "Каргалы", "Жамбыл"] },
|
||||||
|
{ rayon: "Илийский", villages: ["Отеген батыра", "Боралдай", "Ащыбулак"] },
|
||||||
|
{ rayon: "Карасайский", villages: ["Каскелен", "Шамалган", "Айтей"] },
|
||||||
|
{ rayon: "Кегенский", villages: ["Кеген", "Талды", "Жанатай"] },
|
||||||
|
{ rayon: "Райымбекский", villages: ["Нарынкол", "Карасаз", "Жанасу"] },
|
||||||
|
{ rayon: "Талгарский", villages: ["Талгар", "Белбулак", "Достык"] },
|
||||||
|
{ rayon: "Уйгурский", villages: ["Чунджа", "Кетмень", "Ават"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Атырауская", center: [47.1, 51.88],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Атырау", villages: ["Атырау", "Томарлы", "Аксай"] },
|
||||||
|
{ rayon: "Индерский", villages: ["Индербор", "Коктогай", "Орлик"] },
|
||||||
|
{ rayon: "Исатайский", villages: ["Исатай", "Зинеден", "Тущыкудык"] },
|
||||||
|
{ rayon: "Кзылкогинский", villages: ["Кызылкога", "Жамбыл", "Коныстану"] },
|
||||||
|
{ rayon: "Курмангазинский", villages: ["Курмангазы", "Азгир", "Балкудук"] },
|
||||||
|
{ rayon: "Макатский", villages: ["Макат", "Байчунас", "Доссор"] },
|
||||||
|
{ rayon: "Махамбетский", villages: ["Махамбет", "Бейбарыс", "Акжайык"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Западно-Казахстанская", center: [51.23, 51.37],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Уральск", villages: ["Уральск", "Круглоозерное", "Деркул"] },
|
||||||
|
{ rayon: "Акжаикский", villages: ["Тайпак", "Алгабас", "Базартобе"] },
|
||||||
|
{ rayon: "Бокейординский", villages: ["Сайхин", "Бисен", "Каратобе"] },
|
||||||
|
{ rayon: "Бурлинский", villages: ["Аксай", "Пугачево", "Кентубек"] },
|
||||||
|
{ rayon: "Жангалинский", villages: ["Жангала", "Кызылоба", "Копжасар"] },
|
||||||
|
{ rayon: "Жанибекский", villages: ["Жанибек", "Камысты", "Куйгенколь"] },
|
||||||
|
{ rayon: "Казталовский", villages: ["Казталовка", "Караоба", "Болашак"] },
|
||||||
|
{ rayon: "Каратобинский", villages: ["Каратобе", "Жусандой", "Акжол"] },
|
||||||
|
{ rayon: "Сырымский", villages: ["Жымпиты", "Аралтобе", "Кособа"] },
|
||||||
|
{ rayon: "Таскалинский", villages: ["Таскала", "Достык", "Каменка"] },
|
||||||
|
{ rayon: "Теректинский", villages: ["Федоровка", "Тонкерис", "Акжайык"] },
|
||||||
|
{ rayon: "Чингирлауский", villages: ["Чингирлау", "Шынгырлау", "Аксу"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Жамбылская", center: [42.82, 71.43],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Тараз", villages: ["Тараз", "Карасу", "Кулан"] },
|
||||||
|
{ rayon: "Байзакский", villages: ["Сарыкемер", "Коптерек", "Бурыл"] },
|
||||||
|
{ rayon: "Жамбылский", villages: ["Аса", "Каратобе", "Айша-Биби"] },
|
||||||
|
{ rayon: "Жуалынский", villages: ["Курдай", "Актобе", "Кокбастау"] },
|
||||||
|
{ rayon: "Кордайский", villages: ["Кордай", "Масанчи", "Карасу"] },
|
||||||
|
{ rayon: "Меркенский", villages: ["Мерке", "Тау", "Акарал"] },
|
||||||
|
{ rayon: "Мойынкумский", villages: ["Мойынкум", "Бидайколь", "Байтал"] },
|
||||||
|
{ rayon: "Сарысуский", villages: ["Жанатас", "Сарысу", "Байкадам"] },
|
||||||
|
{ rayon: "Таласский", villages: ["Акколь", "Кенен", "Сегизсай"] },
|
||||||
|
{ rayon: "Турара Рыскулова", villages: ["Кулан", "Каменка", "Абай"] },
|
||||||
|
{ rayon: "Шуский", villages: ["Толе би", "Шу", "Акшу"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Жетысуская", center: [45.0, 78.38],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Талдыкорган", villages: ["Талдыкорган", "Еркин", "Каратал"] },
|
||||||
|
{ rayon: "Аксуский", villages: ["Жансугуров", "Аксу", "Мукыр"] },
|
||||||
|
{ rayon: "Алакольский", villages: ["Кабанбай", "Достык", "Кокжар"] },
|
||||||
|
{ rayon: "Ескельдинский", villages: ["Карабулак", "Талдыбулак", "Кайнарлы"] },
|
||||||
|
{ rayon: "Каратальский", villages: ["Уштобе", "Каратал", "Айдарлы"] },
|
||||||
|
{ rayon: "Кербулакский", villages: ["Сарыозек", "Сарыбулак", "Кулык"] },
|
||||||
|
{ rayon: "Коксуский", villages: ["Балпык би", "Коксу", "Майлыбас"] },
|
||||||
|
{ rayon: "Панфиловский", villages: ["Жаркент", "Аухатты", "Коктал"] },
|
||||||
|
{ rayon: "Саркандский", villages: ["Сарканд", "Амангельды", "Караой"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Карагандинская", center: [49.8, 73.09],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Караганда", villages: ["Караганда", "Сортировка", "Кокпекты"] },
|
||||||
|
{ rayon: "Абайский", villages: ["Абай", "Топар", "Карабас"] },
|
||||||
|
{ rayon: "Актогайский", villages: ["Актогай", "Нуркен", "Сарышаган"] },
|
||||||
|
{ rayon: "Бухар-Жырауский", villages: ["Ботакара", "Габидена Мустафина", "Самарканд"] },
|
||||||
|
{ rayon: "Каркаралинский", villages: ["Каркаралинск", "Егиндыбулак", "Коянды"] },
|
||||||
|
{ rayon: "Нуринский", villages: ["Баршино", "Нура", "Кызылжар"] },
|
||||||
|
{ rayon: "Осакаровский", villages: ["Осакаровка", "Молодежный", "Карагайлы"] },
|
||||||
|
{ rayon: "Шетский", villages: ["Коктенколь", "Аксу-Аюлы", "Мойынты"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Костанайская", center: [53.22, 63.62],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Костанай", villages: ["Костанай", "Затобольск", "Ульяновское"] },
|
||||||
|
{ rayon: "Алтынсаринский", villages: ["Убаган", "Докучаевка", "Кутиховка"] },
|
||||||
|
{ rayon: "Амангельдинский", villages: ["Амангельды", "Карасу", "Бидайык"] },
|
||||||
|
{ rayon: "Аулиекольский", villages: ["Аулиеколь", "Диевка", "Сулыколь"] },
|
||||||
|
{ rayon: "Беимбета Майлина", villages: ["Науырзым", "Тарановское", "Баталы"] },
|
||||||
|
{ rayon: "Денисовский", villages: ["Денисовка", "Архангельское", "Глебовка"] },
|
||||||
|
{ rayon: "Жангельдинский", villages: ["Торгай", "Калмакколь", "Айшырык"] },
|
||||||
|
{ rayon: "Житикаринский", villages: ["Житикара", "Кировка", "Степной"] },
|
||||||
|
{ rayon: "Камыстинский", villages: ["Камысты", "Карабатыр", "Аралколь"] },
|
||||||
|
{ rayon: "Карабалыкский", villages: ["Карабалык", "Победа", "Славянка"] },
|
||||||
|
{ rayon: "Карасуский", villages: ["Карасу", "Октябрьское", "Койбагар"] },
|
||||||
|
{ rayon: "Костанайский", villages: ["Тобыл", "Москалевка", "Александровка"] },
|
||||||
|
{ rayon: "Мендыкаринский", villages: ["Боровской", "Каменка", "Первомайское"] },
|
||||||
|
{ rayon: "Сарыкольский", villages: ["Сарыколь", "Большие Дубравы", "Лесной"] },
|
||||||
|
{ rayon: "Узункольский", villages: ["Узунколь", "Ряжское", "Пресногорьковка"] },
|
||||||
|
{ rayon: "Федоровский", villages: ["Федоровка", "Веселоярка", "Пешковка"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Кызылординская", center: [44.85, 65.52],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Кызылорда", villages: ["Кызылорда", "Кызылжарма", "Талсуат"] },
|
||||||
|
{ rayon: "Аральский", villages: ["Аральск", "Жанаконыс", "Аманоткель"] },
|
||||||
|
{ rayon: "Жалагашский", villages: ["Жалагаш", "Торткудык", "Енбек"] },
|
||||||
|
{ rayon: "Казалинский", villages: ["Айтеке би", "Уркендеу", "Кызылкум"] },
|
||||||
|
{ rayon: "Кармакшинский", villages: ["Жосалы", "Далабай", "Торетам"] },
|
||||||
|
{ rayon: "Сырдарьинский", villages: ["Теренозек", "Когалыколь", "Кызылкак"] },
|
||||||
|
{ rayon: "Чиилийский", villages: ["Шиели", "Тартогай", "Балаби"] },
|
||||||
|
{ rayon: "Яныкурганский", villages: ["Жанибек", "Когалы", "Куйган"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Мангистауская", center: [43.65, 51.15],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Актау", villages: ["Актау", "Умирзак", "Сауга"] },
|
||||||
|
{ rayon: "Бейнеуский", villages: ["Бейнеу", "Турыш", "Акжигит"] },
|
||||||
|
{ rayon: "Каракиянский", villages: ["Курык", "Баутино", "Каракия"] },
|
||||||
|
{ rayon: "Мангистауский", villages: ["Жетыбай", "Шетпе", "Аксу"] },
|
||||||
|
{ rayon: "Мунайлинский", villages: ["Мангистау", "Тенге", "Баянды"] },
|
||||||
|
{ rayon: "Тупкараганский", villages: ["Форт-Шевченко", "Таучик", "Баутино"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Павлодарская", center: [52.28, 76.95],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Павлодар", villages: ["Павлодар", "Кенжеколь", "Мойылды"] },
|
||||||
|
{ rayon: "Аккулинский", villages: ["Акку", "Амангельды", "Кызылжар"] },
|
||||||
|
{ rayon: "Актогайский", villages: ["Актогай", "Харьковка", "Куттерген"] },
|
||||||
|
{ rayon: "Баянаульский", villages: ["Баянаул", "Каракамыс", "Бирлик"] },
|
||||||
|
{ rayon: "Железинский", villages: ["Железинка", "Енбекши", "Башмачное"] },
|
||||||
|
{ rayon: "Иртышский", villages: ["Иртышск", "Голубовка", "Панфилово"] },
|
||||||
|
{ rayon: "Майский", villages: ["Коктобе", "Майкарагай", "Кентубек"] },
|
||||||
|
{ rayon: "Павлодарский", villages: ["Ленинский", "Черноярка", "Красноармейка"] },
|
||||||
|
{ rayon: "Теренкольский", villages: ["Теренколь", "Береговое", "Тогузак"] },
|
||||||
|
{ rayon: "Успенский", villages: ["Успенка", "Богодаровка", "Чернорецк"] },
|
||||||
|
{ rayon: "Щербактинский", villages: ["Щербакты", "Галкино", "Алексеевка"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Северо-Казахстанская", center: [54.87, 69.14],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Петропавловск", villages: ["Петропавловск", "Новопавловка", "Бишкуль"] },
|
||||||
|
{ rayon: "Айыртауский", villages: ["Саумалколь", "Имантау", "Лобаново"] },
|
||||||
|
{ rayon: "Акжарский", villages: ["Талшик", "Ленинградское", "Ульгули"] },
|
||||||
|
{ rayon: "Аккайынский", villages: ["Смирново", "Киялы", "Трудовое"] },
|
||||||
|
{ rayon: "Есильский", villages: ["Явленка", "Покровка", "Чкалово"] },
|
||||||
|
{ rayon: "Жамбылский", villages: ["Пресновка", "Май-Балык", "Вознесенка"] },
|
||||||
|
{ rayon: "Кызылжарский", villages: ["Бесколь", "Коньково", "Прибрежное"] },
|
||||||
|
{ rayon: "Магжана Жумабаева", villages: ["Булаево", "Молодогвардейское", "Возвышенка"] },
|
||||||
|
{ rayon: "Мамлютский", villages: ["Мамлютка", "Дубровное", "Покровка"] },
|
||||||
|
{ rayon: "Тайыншинский", villages: ["Тайынша", "Чкалово", "Кирово"] },
|
||||||
|
{ rayon: "Тимирязевский", villages: ["Тимирязево", "Есиль", "Комсомольское"] },
|
||||||
|
{ rayon: "Уалихановский", villages: ["Кишкенеколь", "Акбулак", "Карасу"] },
|
||||||
|
{ rayon: "Шал акына", villages: ["Сергеевка", "Новопокровка", "Пригородное"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Туркестанская", center: [42.33, 69.59],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Туркестан", villages: ["Туркестан", "Акжар", "Коксу"] },
|
||||||
|
{ rayon: "Темирлановка", villages: ["Темирлановка", "Каскелен", "Керейт"] },
|
||||||
|
{ rayon: "Байдибекский", villages: ["Шаян", "Боралдай", "Коктерек"] },
|
||||||
|
{ rayon: "Жетысайский", villages: ["Жетысай", "Асыката", "Ынтымак"] },
|
||||||
|
{ rayon: "Казыгуртский", villages: ["Казыгурт", "Турбат", "Шарбулак"] },
|
||||||
|
{ rayon: "Келесский", villages: ["Абай", "Кошкарата", "Каратобе"] },
|
||||||
|
{ rayon: "Мактааральский", villages: ["Атакент", "Ильичевка", "Макталы"] },
|
||||||
|
{ rayon: "Ордабасынский", villages: ["Темирлановка", "Бугунь", "Караспан"] },
|
||||||
|
{ rayon: "Отырарский", villages: ["Шаульдер", "Караконыр", "Коктобе"] },
|
||||||
|
{ rayon: "Сайрамский", villages: ["Аксукент", "Сайрам", "Манкент"] },
|
||||||
|
{ rayon: "Сарыагашский", villages: ["Сарыагаш", "Жылга", "Дарбаза"] },
|
||||||
|
{ rayon: "Созакский", villages: ["Шолаккорган", "Созак", "Каракур"] },
|
||||||
|
{ rayon: "Толебийский", villages: ["Толе би", "Каратау", "Акжар"] },
|
||||||
|
{ rayon: "Тюлькубасский", villages: ["Тюлькубас", "Майлыкент", "Алатау"] },
|
||||||
|
{ rayon: "Шардаринский", villages: ["Шардара", "Коксу", "Акшенгельды"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Улытауская", center: [47.78, 67.7],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Жезказган", villages: ["Жезказган", "Кенгир", "Сарыкенгир"] },
|
||||||
|
{ rayon: "Жанааркинский", villages: ["Жанаарка", "Актубек", "Байдалы"] },
|
||||||
|
{ rayon: "Улытауский", villages: ["Улытау", "Аксу-Аюлы", "Жезды"] },
|
||||||
|
{ rayon: "Сатпаев", villages: ["Сатпаев", "Карсакпай", "Кенгирское"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Восточно-Казахстанская", center: [49.98, 82.62],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Усть-Каменогорск", villages: ["Усть-Каменогорск", "Ахмирово", "Меновное"] },
|
||||||
|
{ rayon: "Алтайский", villages: ["Перевальное", "Снегирево", "Куйбышево"] },
|
||||||
|
{ rayon: "Глубоковский", villages: ["Глубокое", "Быструха", "Малоубинка"] },
|
||||||
|
{ rayon: "Зайсанский", villages: ["Зайсан", "Карабулак", "Акарал"] },
|
||||||
|
{ rayon: "Катон-Карагайский", villages: ["Катон-Карагай", "Улкен Нарын", "Коробиха"] },
|
||||||
|
{ rayon: "Куршимский", villages: ["Куршим", "Кызылжар", "Маралды"] },
|
||||||
|
{ rayon: "Самарский", villages: ["Самарское", "Каратюбе", "Кокжыра"] },
|
||||||
|
{ rayon: "Тарбагатайский", villages: ["Акжар", "Кокжыра", "Толеугали"] },
|
||||||
|
{ rayon: "Уланский", villages: ["Касыма Кайсенова", "Мамай батыра", "Кабанбай"] },
|
||||||
|
{ rayon: "Шемонаихинский", villages: ["Шемонаиха", "Усть-Таловка", "Первомайский"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Абайская", center: [50.39, 80.25],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Семей", villages: ["Семей", "Карауыл", "Знаменка"] },
|
||||||
|
{ rayon: "Абайский", villages: ["Карауыл", "Кокбай", "Кунанбай"] },
|
||||||
|
{ rayon: "Аягозский", villages: ["Аягоз", "Тарлаулы", "Мадениет"] },
|
||||||
|
{ rayon: "Бескарагайский", villages: ["Бескарагай", "Долонь", "Канонерка"] },
|
||||||
|
{ rayon: "Бородулихинский", villages: ["Бородулиха", "Новопокровка", "Новодворовка"] },
|
||||||
|
{ rayon: "Жанасемейский", villages: ["Кайнар", "Коркыт", "Каменка"] },
|
||||||
|
{ rayon: "Кокпектинский", villages: ["Кокпекты", "Тасшокы", "Кокжайык"] },
|
||||||
|
{ rayon: "Урджарский", villages: ["Урджар", "Каракол", "Маканчи"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Астана", center: [51.13, 71.43],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Алматинский", villages: ["Астана", "Аксу", "Интернациональный"] },
|
||||||
|
{ rayon: "Байконурский", villages: ["Астана", "Куйгенжар", "Каражар"] },
|
||||||
|
{ rayon: "Есильский", villages: ["Астана", "Зеленый Квартал", "Жагалау"] },
|
||||||
|
{ rayon: "Сарыаркинский", villages: ["Астана", "Коктал", "Пригородное"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Алматы", center: [43.24, 76.89],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Алмалы", villages: ["Алматы", "Каменка", "Гульдала"] },
|
||||||
|
{ rayon: "Ауэзов", villages: ["Алматы", "Калкаман", "Теректы"] },
|
||||||
|
{ rayon: "Бостандык", villages: ["Алматы", "Нур-Алатау", "Карагайлы"] },
|
||||||
|
{ rayon: "Жетысу", villages: ["Алматы", "Айнабулак", "Коктобе"] },
|
||||||
|
{ rayon: "Медеу", villages: ["Алматы", "Кумбель", "Бутаковка"] },
|
||||||
|
{ rayon: "Наурызбай", villages: ["Алматы", "Таугуль", "Калкаман"] },
|
||||||
|
{ rayon: "Турксиб", villages: ["Алматы", "Жулдыз", "Отеген"] },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oblast: "Шымкент", center: [42.32, 69.60],
|
||||||
|
rayons: [
|
||||||
|
{ rayon: "Абай", villages: ["Шымкент", "Сайрам", "Каратобе"] },
|
||||||
|
{ rayon: "Енбекши", villages: ["Шымкент", "Туран", "Жанаталап"] },
|
||||||
|
{ rayon: "Каратау", villages: ["Шымкент", "Кызылжар", "Тайбурыл"] },
|
||||||
|
{ rayon: "Аль-Фараби", villages: ["Шымкент", "Акжайык", "Казына"] },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
Loading…
Reference in New Issue
Block a user