api/templates/main/vector-api-guide.html.twig
2026-03-17 12:30:28 +00:00

565 lines
31 KiB
Twig
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends 'base.html.twig' %}
{% block title %}راهنمای API نقشه وکتوری (MVT) | می‌مپس{% endblock %}
{% block description %}راهنمای کامل استفاده از API تایل‌های وکتوری (MVT) می‌مپس - جاده‌ها، ساختمان‌ها و نقاط با Leaflet.VectorGrid و MapLibre{% endblock %}
{% block keywords %}API وکتور, MVT, نقشه وکتوری, تایل وکتوری, Leaflet VectorGrid, MapLibre, API نقشه وکتوری{% endblock %}
{% block og_title %}راهنمای API نقشه وکتوری (MVT) می‌مپس{% endblock %}
{% block og_description %}راهنمای کامل استفاده از API تایل‌های وکتوری می‌مپس با لایه‌های جاده، ساختمان و نقاط{% endblock %}
{% block body %}
<!-- Hero Section -->
<section class="hero-section text-white">
<div class="container">
<div class="text-center">
<h1 class="display-4 fw-bold mb-4">
راهنمای API نقشه وکتوری (MVT)
<span class="d-block">سرویس می‌مپس</span>
</h1>
<p class="lead opacity-90">
تایل‌های وکتوری Mapbox Vector Tiles با داده OpenStreetMap و محدودیت جغرافیایی ایران
</p>
</div>
</div>
</section>
<!-- Table of Contents -->
<section class="py-5 bg-light">
<div class="container">
<div class="card border-0 shadow-sm">
<div class="card-body p-4">
<h2 class="h3 fw-bold mb-4">فهرست مطالب</h2>
<div class="row g-3">
<div class="col-lg-4 col-md-6">
<a href="#introduction" class="text-decoration-none">
<div class="card h-100 border-0 shadow-sm hover-card">
<div class="card-body text-center p-3">
<div class="bg-primary text-white rounded-circle d-inline-flex align-items-center justify-content-center mb-2" style="width: 40px; height: 40px;">
<span class="fw-bold">1</span>
</div>
<h6 class="card-title mb-0 text-dark">معرفی API</h6>
</div>
</div>
</a>
</div>
<div class="col-lg-4 col-md-6">
<a href="#features" class="text-decoration-none">
<div class="card h-100 border-0 shadow-sm hover-card">
<div class="card-body text-center p-3">
<div class="bg-success text-white rounded-circle d-inline-flex align-items-center justify-content-center mb-2" style="width: 40px; height: 40px;">
<span class="fw-bold">2</span>
</div>
<h6 class="card-title mb-0 text-dark">ویژگی‌ها</h6>
</div>
</div>
</a>
</div>
<div class="col-lg-4 col-md-6">
<a href="#endpoint" class="text-decoration-none">
<div class="card h-100 border-0 shadow-sm hover-card">
<div class="card-body text-center p-3">
<div class="bg-warning text-white rounded-circle d-inline-flex align-items-center justify-content-center mb-2" style="width: 40px; height: 40px;">
<span class="fw-bold">3</span>
</div>
<h6 class="card-title mb-0 text-dark">نقطه پایانی</h6>
</div>
</div>
</a>
</div>
<div class="col-lg-4 col-md-6">
<a href="#layers" class="text-decoration-none">
<div class="card h-100 border-0 shadow-sm hover-card">
<div class="card-body text-center p-3">
<div class="bg-info text-white rounded-circle d-inline-flex align-items-center justify-content-center mb-2" style="width: 40px; height: 40px;">
<span class="fw-bold">4</span>
</div>
<h6 class="card-title mb-0 text-dark">لایه‌ها</h6>
</div>
</div>
</a>
</div>
<div class="col-lg-4 col-md-6">
<a href="#iran-restriction" class="text-decoration-none">
<div class="card h-100 border-0 shadow-sm hover-card">
<div class="card-body text-center p-3">
<div class="bg-secondary text-white rounded-circle d-inline-flex align-items-center justify-content-center mb-2" style="width: 40px; height: 40px;">
<span class="fw-bold">5</span>
</div>
<h6 class="card-title mb-0 text-dark">محدودیت ایران</h6>
</div>
</div>
</a>
</div>
<div class="col-lg-4 col-md-6">
<a href="#examples" class="text-decoration-none">
<div class="card h-100 border-0 shadow-sm hover-card">
<div class="card-body text-center p-3">
<div class="bg-dark text-white rounded-circle d-inline-flex align-items-center justify-content-center mb-2" style="width: 40px; height: 40px;">
<span class="fw-bold">6</span>
</div>
<h6 class="card-title mb-0 text-dark">مثال‌های عملی</h6>
</div>
</div>
</a>
</div>
<div class="col-lg-4 col-md-6">
<a href="#cache" class="text-decoration-none">
<div class="card h-100 border-0 shadow-sm hover-card">
<div class="card-body text-center p-3">
<div class="bg-primary text-white rounded-circle d-inline-flex align-items-center justify-content-center mb-2" style="width: 40px; height: 40px;">
<span class="fw-bold">7</span>
</div>
<h6 class="card-title mb-0 text-dark">کش و اطلاعات</h6>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Introduction Section -->
<section id="introduction" class="py-5">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="text-center mb-5">
<h2 class="display-5 fw-bold text-gradient mb-3">تایل‌های وکتوری (MVT) از می‌مپس</h2>
<p class="lead text-muted">
API نقشه وکتوری می‌مپس تایل‌های Mapbox Vector Tiles را با داده OpenStreetMap و محدودیت به ایران ارائه می‌دهد
</p>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-body p-4">
<div class="alert alert-info mb-4">
<h5 class="alert-heading">چرا API وکتور می‌مپس؟</h5>
<ul class="mb-0 mt-2">
<li>🇮🇷 <strong>محدود به ایران:</strong> تایل‌ها فقط برای محدوده ایران از PostGIS/OSM تولید می‌شوند؛ خارج از ایران پاسخ 204 No Content</li>
<li>📐 <strong>فرمت MVT:</strong> استاندارد Mapbox Vector Tiles سازگار با Leaflet.VectorGrid، MapLibre و OpenLayers</li>
<li>⚡ <strong>کش خودکار:</strong> تایل‌های تولیدشده در کش ذخیره و در درخواست‌های بعدی سریع سرو می‌شوند</li>
<li>🗺️ <strong>سه لایه:</strong> جاده‌ها (roads)، ساختمان‌ها (buildings)، نقاط (points) از داده OSM</li>
</ul>
</div>
<div class="alert alert-success">
<h5 class="alert-heading">مزایای سرویس</h5>
<ul class="mb-0 mt-2">
<li>✅ <strong>سرور داخل کشور:</strong> سرو تایل از اینترنت داخلی</li>
<li>✅ <strong>استایل‌پذیری:</strong> وکتور در سمت کلاینت قابل استایل است</li>
<li>✅ <strong>پسوند .mvt و .pbf:</strong> هر دو پشتیبانی می‌شوند</li>
<li>✅ <strong>پشتیبانی CORS:</strong> استفاده مستقیم از مرورگر</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Features Section -->
<section id="features" class="py-5 bg-light">
<div class="container">
<div class="text-center mb-5">
<h2 class="display-5 fw-bold text-gradient mb-3">ویژگی‌های کلیدی</h2>
<p class="lead text-muted">
MVT، محدودیت ایران و سازگاری با کتابخانه‌های نقشه
</p>
</div>
<div class="row g-4">
<div class="col-lg-6">
<div class="card h-100 border-0 shadow-sm">
<div class="card-header bg-success text-white">
<h5 class="mb-0">📐 فرمت MVT</h5>
</div>
<div class="card-body">
<p class="card-text">
تایل‌ها به صورت Mapbox Vector Tiles (پروتوباف) با extent 4096 تولید می‌شوند و با Leaflet.VectorGrid، MapLibre GL JS و OpenLayers سازگار هستند.
</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100 border-0 shadow-sm">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">🇮🇷 محدودیت ایران</h5>
</div>
<div class="card-body">
<p class="card-text">
فقط تایل‌هایی که با محدوده جغرافیایی ایران تلاقی دارند از دیتابیس خوانده می‌شوند. برای نواحی خارج از ایران پاسخ <strong>204 No Content</strong> برگردانده می‌شود.
</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100 border-0 shadow-sm">
<div class="card-header bg-warning text-dark">
<h5 class="mb-0">🗺️ لایه‌های جدا</h5>
</div>
<div class="card-body">
<p class="card-text">
سه لایه مستقل: <strong>roads</strong> (جاده‌ها از planet_osm_line)، <strong>buildings</strong> (ساختمان‌ها از planet_osm_polygon)، <strong>points</strong> (نقاط POI از planet_osm_point). زوم ۰ تا ۲۲.
</p>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100 border-0 shadow-sm">
<div class="card-header bg-info text-white">
<h5 class="mb-0">💡 کش</h5>
</div>
<div class="card-body">
<p class="card-text">
تایل‌های تولیدشده در <code>var/cache/vector-tiles</code> ذخیره می‌شوند. درخواست‌های بعدی همان تایل از کش پاسخ داده می‌شوند.
</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Endpoint Section -->
<section id="endpoint" class="py-5">
<div class="container">
<div class="text-center mb-5">
<h2 class="display-5 fw-bold text-gradient mb-3">نقطه پایانی API</h2>
<p class="lead text-muted">
آدرس و پارامترهای API تایل‌های وکتوری
</p>
</div>
<div class="row g-4">
<div class="col-lg-8 mx-auto">
<div class="card border-0 shadow-sm">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">نقطه پایانی دریافت تایل وکتوری</h5>
</div>
<div class="card-body">
<div class="bg-dark text-light p-4 rounded text-end" dir="ltr">
<strong>URL:</strong><br>
<code class="text-info">https://memaps.ir/api/vector/{layer}/{z}/{x}/{y}.{ext}</code><br><br>
<strong>Method:</strong><br>
<code class="text-success">GET</code><br><br>
<strong>Content-Type:</strong><br>
<code class="text-warning">application/vnd.mapbox-vector-tile</code>
</div>
<div class="mt-4">
<h6 class="fw-bold mb-3">پارامترهای URL:</h6>
<div class="table-responsive">
<table class="table table-striped">
<thead class="table-dark">
<tr>
<th>پارامتر</th>
<th>توضیح</th>
<th>مثال</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>{layer}</code></td>
<td>نام لایه: roads، buildings، points</td>
<td><code>roads</code></td>
</tr>
<tr>
<td><code>{z}</code></td>
<td>سطح زوم (۰ تا ۲۲)</td>
<td><code>16</code></td>
</tr>
<tr>
<td><code>{x}</code></td>
<td>مختصات X تایل (XYZ)</td>
<td><code>41240</code></td>
</tr>
<tr>
<td><code>{y}</code></td>
<td>مختصات Y تایل (XYZ)</td>
<td><code>26154</code></td>
</tr>
<tr>
<td><code>{ext}</code></td>
<td>پسوند: mvt یا pbf (هر دو یکسان)</td>
<td><code>pbf</code> / <code>mvt</code></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="mt-4">
<h6 class="fw-bold mb-3">مثال‌های URL:</h6>
<pre class="bg-light p-3 rounded text-end" dir="ltr"><code>// جاده‌ها
GET https://memaps.ir/api/vector/roads/16/41240/26154.pbf
// ساختمان‌ها
GET https://memaps.ir/api/vector/buildings/16/41240/26154.mvt
// نقاط (POI)
GET https://memaps.ir/api/vector/points/10/512/512.pbf</code></pre>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Layers Section -->
<section id="layers" class="py-5 bg-light">
<div class="container">
<div class="text-center mb-5">
<h2 class="display-5 fw-bold text-gradient mb-3">لایه‌های وکتوری</h2>
<p class="lead text-muted">
سه لایه جدا با نام لایه (source-layer) یکسان با نام مسیر
</p>
</div>
<div class="row g-4">
<div class="col-lg-4">
<div class="card h-100 border-0 shadow-sm">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">🛣️ جاده‌ها (roads)</h5>
</div>
<div class="card-body">
<p class="card-text">خطوط جاده از <code>planet_osm_line</code> با فیلتر <code>highway IS NOT NULL</code>. ویژگی‌ها: osm_id، name، highway.</p>
<div class="bg-dark text-light p-2 rounded text-end small" dir="ltr">
<code>.../vector/roads/{z}/{x}/{y}.pbf</code>
</div>
<p class="small text-muted mt-2 mb-0">source-layer: <code>roads</code></p>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card h-100 border-0 shadow-sm">
<div class="card-header bg-success text-white">
<h5 class="mb-0">🏢 ساختمان‌ها (buildings)</h5>
</div>
<div class="card-body">
<p class="card-text">پلیگون ساختمان از <code>planet_osm_polygon</code> با فیلتر <code>building IS NOT NULL</code>. ویژگی‌ها: osm_id، name، building.</p>
<div class="bg-dark text-light p-2 rounded text-end small" dir="ltr">
<code>.../vector/buildings/{z}/{x}/{y}.pbf</code>
</div>
<p class="small text-muted mt-2 mb-0">source-layer: <code>buildings</code></p>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card h-100 border-0 shadow-sm">
<div class="card-header bg-info text-white">
<h5 class="mb-0">📍 نقاط (points)</h5>
</div>
<div class="card-body">
<p class="card-text">نقاط POI از <code>planet_osm_point</code> با نام یا amenity/shop/tourism. ویژگی‌ها: osm_id، name، amenity، shop، tourism.</p>
<div class="bg-dark text-light p-2 rounded text-end small" dir="ltr">
<code>.../vector/points/{z}/{x}/{y}.pbf</code>
</div>
<p class="small text-muted mt-2 mb-0">source-layer: <code>points</code></p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Iran Restriction Section -->
<section id="iran-restriction" class="py-5">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card border-0 shadow-sm">
<div class="card-header bg-secondary text-white">
<h5 class="mb-0">🇮🇷 محدودیت جغرافیایی به ایران</h5>
</div>
<div class="card-body p-4">
<p class="card-text">
API وکتور می‌مپس <strong>فقط برای محدوده ایران</strong> تایل تولید می‌کند:
</p>
<ul>
<li>اگر تایل با مرزهای ایران (تقریبی) <strong>تلاقی داشته باشد</strong>: از کش خوانده می‌شود یا از PostGIS تولید، کش و برگردانده می‌شود.</li>
<li>اگر تایل <strong>خارج از ایران</strong> باشد: پاسخ <strong>204 No Content</strong> (بدون بدنه) برگردانده می‌شود و به دیتابیس درخواستی ارسال نمی‌شود.</li>
</ul>
<div class="alert alert-info mb-0">
کتابخانه‌های نقشه (Leaflet.VectorGrid، MapLibre) معمولاً 204 را به‌عنوان «تایل خالی» می‌پذیرند و خطا نشان نمی‌دهند.
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Examples Section -->
<section id="examples" class="py-5 bg-light">
<div class="container">
<div class="text-center mb-5">
<h2 class="display-5 fw-bold text-gradient mb-3">مثال‌های عملی</h2>
<p class="lead text-muted">
استفاده با Leaflet.VectorGrid و MapLibre GL JS
</p>
</div>
<div class="row g-4">
<div class="col-lg-6">
<div class="card h-100 border-0 shadow-sm">
<div class="card-header bg-success text-white">
<h5 class="mb-0">Leaflet + VectorGrid - لایه جاده‌ها</h5>
</div>
<div class="card-body">
<pre class="bg-dark text-light p-3 rounded text-end" dir="ltr" style="font-size: 0.85rem;"><code>&lt;script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"&gt;&lt;/script&gt;
&lt;script src="https://unpkg.com/leaflet.vectorgrid@1.3.0/dist/Leaflet.VectorGrid.bundled.js"&gt;&lt;/script&gt;
&lt;script&gt;
var map = L.map('map').setView([35.6892, 51.3890], 10);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
var roads = L.vectorGrid.protobuf('https://memaps.ir/api/vector/roads/{z}/{x}/{y}.pbf', {
vectorTileLayerStyles: {
roads: { color: '#555', weight: 1.5 }
},
interactive: true
}).addTo(map);
&lt;/script&gt;</code></pre>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100 border-0 shadow-sm">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">Leaflet - سه لایه روی هم</h5>
</div>
<div class="card-body">
<pre class="bg-dark text-light p-3 rounded text-end" dir="ltr" style="font-size: 0.85rem;"><code>var base = 'https://memaps.ir/api/vector/';
var roads = L.vectorGrid.protobuf(base + 'roads/{z}/{x}/{y}.pbf', {
vectorTileLayerStyles: { roads: { color: '#555', weight: 1.5 } }
});
var buildings = L.vectorGrid.protobuf(base + 'buildings/{z}/{x}/{y}.pbf', {
vectorTileLayerStyles: { buildings: { fillColor: '#ddd', color: '#888', weight: 0.8 } }
});
var points = L.vectorGrid.protobuf(base + 'points/{z}/{x}/{y}.pbf', {
vectorTileLayerStyles: { points: { radius: 4, color: '#c00', fillColor: '#f66' } }
});
map.addLayer(roads);
map.addLayer(buildings);
map.addLayer(points);</code></pre>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100 border-0 shadow-sm">
<div class="card-header bg-warning text-dark">
<h5 class="mb-0">MapLibre GL JS - جاده‌ها</h5>
</div>
<div class="card-body">
<pre class="bg-dark text-light p-3 rounded text-end" dir="ltr" style="font-size: 0.85rem;"><code>map.addSource('roads', {
type: 'vector',
tiles: ['https://memaps.ir/api/vector/roads/{z}/{x}/{y}.pbf'],
maxzoom: 22
});
map.addLayer({
id: 'roads-layer',
type: 'line',
source: 'roads',
'source-layer': 'roads',
paint: { 'line-color': '#555', 'line-width': 1.5 }
});</code></pre>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card h-100 border-0 shadow-sm">
<div class="card-header bg-info text-white">
<h5 class="mb-0">دریافت مستقیم (fetch)</h5>
</div>
<div class="card-body">
<pre class="bg-dark text-light p-3 rounded text-end" dir="ltr" style="font-size: 0.85rem;"><code>async function getVectorTile(layer, z, x, y) {
const url = `https://memaps.ir/api/vector/${layer}/${z}/${x}/${y}.pbf`;
const res = await fetch(url);
if (res.status === 204) return null; // خارج از ایران
if (!res.ok) throw new Error(res.statusText);
return await res.arrayBuffer();
}
getVectorTile('roads', 16, 41240, 26154);</code></pre>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Cache Section -->
<section id="cache" class="py-5">
<div class="container">
<div class="text-center mb-5">
<h2 class="display-5 fw-bold text-gradient mb-3">کش و اطلاعات</h2>
<p class="lead text-muted">
کش تایل‌های وکتوری و endpoint اطلاعات کش
</p>
</div>
<div class="row g-4">
<div class="col-lg-8 mx-auto">
<div class="card border-0 shadow-sm">
<div class="card-body p-4">
<h3 class="h4 fw-bold mb-4">نحوه کار</h3>
<ol class="mb-4">
<li class="mb-2"><strong>بررسی محدوده ایران:</strong> اگر تایل خارج از ایران باشد، بلافاصله پاسخ 204 No Content برگردانده می‌شود.</li>
<li class="mb-2"><strong>بررسی کش:</strong> اگر تایل داخل ایران است، وجود آن در <code>var/cache/vector-tiles/{layer}/{z}/{x}/{y}.mvt</code> بررسی می‌شود.</li>
<li class="mb-2"><strong>بازگشت از کش:</strong> در صورت وجود در کش، همان فایل MVT به کاربر برگردانده می‌شود.</li>
<li class="mb-2"><strong>تولید از PostGIS:</strong> در صورت نبود در کش، تایل با ST_AsMVT از دیتابیس OSM تولید، در کش ذخیره و برگردانده می‌شود.</li>
</ol>
<div class="alert alert-success">
<strong>اطلاعات کش:</strong><br>
<code dir="ltr">GET https://memaps.ir/api/vector/cache/info</code><br>
<span class="text-muted">خروجی JSON شامل ، total_size، total_size_bytes و file_count است.</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- CORS Section -->
<section class="py-5 bg-light">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card border-0 shadow-sm">
<div class="card-header bg-success text-white">
<h5 class="mb-0">پشتیبانی از CORS</h5>
</div>
<div class="card-body">
<p class="card-text">
API وکتور می‌مپس از CORS پشتیبانی می‌کند و می‌توانید مستقیماً از مرورگر و دامنه‌های دیگر استفاده کنید.
</p>
</div>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block stylesheets %}
<style>
.hover-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.hover-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0,0,0,0.1) !important;
}
pre { font-size: 0.9rem; line-height: 1.4; }
code { font-family: 'Courier New', monospace; }
</style>
{% endblock %}