Салбарын газрын зураг
Банк эсвэл эмийн сангийн сүлжээний салбарууд демо дүүрэгт. Хүн очиж баталгаажуулсан хаалга ногоон, бусад нь саарал, хаалга биш.
Код
html
<!doctype html>
<!--
dock sample app: outlet map. A public chain's branches (a bank, a pharmacy) in the demo district (PLAN.md 21:
west 106.80, south 47.85, east 106.95, north 47.92), the stand-in for a distributor's outlets. Type the chain's name;
every place of that name inside the district is listed and drawn. A branch is a door only when a person stood at it
and confirmed it: then it has a code. Until then it is shown grey and "албан бус", never as a door. The outlets JSON
is printed for your system. Branches come from the names dock holds, not from the chain's own list: say so to them.
Serve sdk/js over http (npm run build, then e.g. `npx serve .`) and open /examples/outlet.html#key=dk_...
The key needs geocode:read and tiles:read; a free key has them.
-->
<html lang="mn">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>dock: салбарын газрын зураг</title>
<link href="https://dock.mn/sdk/v1/dock.css" rel="stylesheet">
<script src="https://dock.mn/sdk/v1/dock.js"></script>
<style>
html, body { margin: 0; height: 100%; font: 14px system-ui, sans-serif; }
body { display: grid; grid-template-columns: 420px 1fr; }
aside { padding: 12px; overflow: auto; border-right: 1px solid #ddd; }
#map { height: 100%; }
input { width: 100%; box-sizing: border-box; padding: 6px; font: inherit; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
td, th { padding: 4px; border-bottom: 1px solid #eee; text-align: left; vertical-align: top; }
tbody tr { cursor: pointer; }
tbody tr:hover { background: #eef; }
.door { color: #15803d; font-weight: 600; }
.guess { color: #6b7280; }
pre { background: #f6f6f6; padding: 8px; overflow: auto; font-size: 12px; }
@media (max-width: 700px) { body { grid-template-columns: 1fr; grid-template-rows: auto 60vh; } }
</style>
</head>
<body>
<aside>
<form id="chain">
<label>Сүлжээний нэр<input name="q" list="chains" value="Голомт банк"></label>
<datalist id="chains"><option>Голомт банк</option><option>Хаан банк</option><option>Худалдаа хөгжлийн банк</option><option>Төрийн банк</option><option>Монос эмийн сан</option></datalist>
<button>Харуулах</button>
</form>
<p id="sum"></p>
<p id="src" class="guess"></p>
<table><thead><tr><th>#</th><th>Салбар</th><th>Хаалга</th></tr></thead><tbody id="rows"></tbody></table>
<p><button id="export" type="button" hidden>Салбаруудын JSON</button></p>
<pre id="out" hidden></pre>
</aside>
<div id="map"></div>
<script type="module">
const { maplibregl } = dock;
const { Client, DockError, createMap, label } = dock;
// The demo district (PLAN.md 21), [west, south, east, north].
const DISTRICT = [106.80, 47.85, 106.95, 47.92];
const CENTER = [(DISTRICT[0] + DISTRICT[2]) / 2, (DISTRICT[1] + DISTRICT[3]) / 2];
const params = new URLSearchParams(location.hash.slice(1));
const client = new Client({ key: 'dk_...' });
const map = createMap(client, { container: 'map', center: CENTER, zoom: 13 });
map.addControl(new maplibregl.NavigationControl());
const $ = (id) => document.getElementById(id);
const form = $('chain');
let markers = [], outlets = [];
const inside = ([lon, lat]) => lon >= DISTRICT[0] && lon <= DISTRICT[2] && lat >= DISTRICT[1] && lat <= DISTRICT[3];
const norm = (s) => (s ?? '').toLowerCase().replace(/\s+/g, ' ').trim();
// A branch carries the chain's name at its start: "Голомт банк" keeps "Голомт банк Зайсан", drops "Голомт хотхон".
const ofChain = (p, chain) => [label(p, 'mn'), label(p, 'en')].some((n) => norm(n).startsWith(chain));
// Only a code means a person confirmed the door; everything else is a place of that name, not a door.
async function show(name) {
const chain = norm(name);
if (!chain) return;
$('sum').textContent = '...';
const { results } = await client.geocode(name, { limit: 20, proximity: CENTER, types: ['door', 'business', 'building', 'landmark'] });
outlets = results.filter((p) => inside(p.point) && ofChain(p, chain));
markers.forEach((m) => m.remove());
markers = outlets.map((p) => new maplibregl.Marker({ color: p.code ? '#15803d' : '#9ca3af' })
.setLngLat(p.point).setPopup(new maplibregl.Popup().setText(label(p))).addTo(map));
$('rows').replaceChildren(...outlets.map((p, i) => {
const tr = document.createElement('tr');
const door = document.createElement('td');
door.className = p.code ? 'door' : 'guess';
door.textContent = p.code ?? '';
tr.append(Object.assign(document.createElement('td'), { textContent: String(i + 1) }),
Object.assign(document.createElement('td'), { textContent: label(p) }), door);
tr.onclick = () => { map.flyTo({ center: p.point, zoom: 17 }); markers[i].togglePopup(); };
return tr;
}));
const doors = outlets.filter((p) => p.code).length;
const official = outlets.filter((p) => !p.unofficial).length;
$('sum').textContent = outlets.length
? `${name}: демо дүүрэгт ${outlets.length} салбар, баталгаажсан хаалга ${doors}, албан ёсны хаягтай ${official}.`
: `${name}: демо дүүрэгт салбар олдсонгүй.`;
$('src').textContent = outlets.length
? `Эх сурвалж: ${[...new Set(outlets.map((p) => p.source ?? 'тодорхойгүй'))].join(', ')}. Сүлжээний өөрийн жагсаалт биш; саарал нь хаалга биш, газрын нэр.`
: '';
$('export').hidden = !outlets.length;
$('out').hidden = true;
map.fitBounds([[DISTRICT[0], DISTRICT[1]], [DISTRICT[2], DISTRICT[3]]], { padding: 20 });
}
// What your system keeps per outlet: the code when the door is confirmed, the official address when matched.
$('export').onclick = () => {
$('out').hidden = false;
$('out').textContent = JSON.stringify(outlets.map((p) => ({
label_mn: label(p, 'mn'), label_en: label(p, 'en'), point: p.point,
door_code: p.code ?? null, door_confirmed: Boolean(p.code),
official_address: p.official_address, unofficial: p.unofficial, source: p.source,
})), null, 2);
};
const fail = (e) => { $('sum').textContent = e instanceof DockError ? `${e.code}: ${e.message}` : String(e); };
form.addEventListener('submit', (ev) => { ev.preventDefault(); show(form.q.value).catch(fail); });
map.on('load', () => {
map.addSource('district', { type: 'geojson', data: { type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [[
[DISTRICT[0], DISTRICT[1]], [DISTRICT[2], DISTRICT[1]], [DISTRICT[2], DISTRICT[3]], [DISTRICT[0], DISTRICT[3]], [DISTRICT[0], DISTRICT[1]]]] } } });
map.addLayer({ id: 'district', type: 'line', source: 'district', paint: { 'line-color': '#1d4ed8', 'line-width': 2, 'line-dasharray': [2, 2] } });
show(form.q.value).catch(fail);
});
</script>
</body>
</html>