نقشه بکشید. نقشه لغزنده با کاشیها.
نقشه بکشید. نقشه لغزنده با کاشیها.
در این مقاله، میخواهیم نشان دهیم که چگونه با استفاده از کتابخانه Aspose.GIS و دادههای عمومی، میتوان یک نقشه لغزنده ساخت که بهصورت لحظهای تولید شود. به لطف ویژگی جدید کتابخانه، اکنون میتوانیم دادههای GIS را از پایگاه داده از طریق پرسوجوی SQL بازیابی کنیم. این نتیجهای است که باید بدست آوریم:
مخزن کد منبع اینجا.
آمادهسازی دادهها.
اول از همه، به اطلاعات جغرافیایی نیاز داریم که بتوانیم آن را در پایگاه داده بارگیری کنیم. یکی از منابع محبوب این نوع اطلاعات OpenStreetMap
است، بنابراین بیایید از آن استفاده کنیم. راحتترین راه، به نظر من، استخراج دادهها در قالب pbf از منبع عمومی https://download.geofabrik.de/ است. برای مثال، بیایید مجارستان را دانلود کنیم.
در مرحله بعدی، به یک نمونه کارکردن PostGIS
نیاز داریم. البته میتوانید از نسخه نصب شده محلی PostgreSQL
استفاده کنید، اما من استفاده از کانتینرهای Docker را بسیار راحت میدانم. بیایید PostGIS را با استفاده از فایل docker compose نصب کنیم:
services:
postgis:
image: postgis/postgis
environment:
- POSTGRES_DB=gis
- POSTGRES_USER=gis
- POSTGRES_PASSWORD=password
ports:
- 5432:5432
volumes:
- d:\\local_folder:/usr/share/gisdata
حجم d:\local_folder:/usr/share/gisdata
برای بارگیری دادههای GIS از ماشین محلی مورد نیاز است.
سپس، بیایید کانتینر خود را اجرا کنیم:
docker compose up
با استفاده از pgAdmin
به نمونه پایگاه داده متصل شوید و پایگاه داده مجارستان را در آنجا ایجاد کنید:
یا از طریق دستور SQL:
CREATE DATABASE Hungary;
افزونههای لازم را به این پایگاه داده اضافه کنید:
اینها افزونههای postgis
و hstore
خواهند بود. hstore یک افزونه است که به شما امکان میدهد از نوع داده کلید-مقدار استفاده کنید. OpenStreetMap بهطور گسترده از این نوع برای توصیف ویژگیهایی استفاده میکند که در دسته اصلی قرار نمیگیرند، بنابراین فیلدهای جداگانهای برای آنها ایجاد نمیشود، اما در فیلد برچسبها ذخیره میشوند.
همچنین یک نسخه شبیه به SQL از دستورات وجود دارد:
CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS postgis;
اکنون بیایید به کانتینر متصل شویم، در مورد من local_folder-postgis-1
است:
docker exec -it local_folder-postgis-1 sh
و برنامهای را که دادهها را از فایل pbf
وارد پایگاه داده میکند نصب کنید:
apt-get update && apt-get install -y osm2pgsql
مطمئن شوید که فایل hungary-latest.osm.pbf
در پوشه local_folder
شما قرار دارد و سپس دستور import را اجرا کنید:
osm2pgsql --create --database=Hungary --user=gis --password --host=localhost --port=5432 --hstore /usr/share/gisdata/hungary-latest.osm.pbf
در مورد مجارستان، تکمیل این دستور یک و نیم دقیقه طول کشید. گزینه --create
به معنای حالت ایجاد ساده پایگاه داده جدید است. بهعلاوه، علاوه بر همه موارد دیگر، حالت --append
نیز وجود دارد که امکان بهروزرسانی دادهها در صورت تغییر آنها را فراهم میکند:
osm2pgsql --append --slim OSMFILE
گزینه --hstore
به برنامه میگوید که یک فیلد tags
از نوع hstore را بهطور اضافی برای ذخیره اطلاعات اضافی در مورد ویژگیها و هندسهها ایجاد کند.
بکاند
بنابراین، دادههای ما آماده استفاده هستند. مرحله بعدی در مسیر ایجاد نقشه، ایجاد بکاند است. هدف از بکاند ما تولید کاشیهای خاص، معمولاً به اندازه 256*256
است که از آنها، مانند یک موزائیک، نقشه در مرورگر جمع میشود. هر کاشی بهطور منحصربهفرد با ترکیبی از پارامترهایی مانند Z که درجه بزرگنمایی/کوچکنمایی نقشه است، X ردیف در آرایه کاشی و Y ستون شناسایی میشود. میتوانید اطلاعات بیشتری در مورد ماهیت کاشیها اینجا بخوانید.
بکاند ما بهطور متناوب بر روی ASP.NET Core خواهد بود، بنابراین بیایید با ایجاد پروژه شروع کنیم. پس بیایید پروژهای را بر اساس الگوی از پیش نصب شده ASP.NET Core MVC
در Visual Studio
ایجاد کنیم.
سپس بسته NuGet Aspose.GIS
را در پروژهای که کاشیها را تولید میکند نصب کنید:
dotnet add package Aspose.GIS --version 24.6.0
پروژه را از فایلهای غیرضروری پاک کنید. بنابراین ساختار آن تقریباً مانند تصویر زیر باشد:
سپس محتویات پوشه wwwroot/lib را حذف کنید، زیرا وابستگیهای ما از طریق libman
نصب میشوند. ساختار فایل libman.json
در زیر آمده است:
{
"version": "1.0",
"defaultProvider": "cdnjs",
"libraries": [
{
"library": "leaflet@1.9.4",
"destination": "wwwroot/lib/leaflet/"
},
{
"library": "bootstrap@5.3.3",
"destination": "wwwroot/lib/bootstrap/",
"files": [
"css/bootstrap-reboot.min.css"
]
}
]
}
وابستگیهای کلاینت را در فایل _Layout.cshtml
اضافه کنید:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Aspose.GIS.TilesTest</title>
<link href="~/lib/bootstrap/css/bootstrap-reboot.min.css" rel="stylesheet" />
@await RenderSectionAsync("Styles", required: false)
</head>
<body>
@RenderBody()
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
و همچنین فایل Index.cshtml
را ویرایش کنید:
@{
ViewData["Title"] = "Home Page";
}
<div id="map"></div>
@section Styles {
<link href="~/lib/leaflet/leaflet.min.css" rel="stylesheet" />
<link href="~/css/map.css" rel="stylesheet" asp-append-version="true"/>
}
@section Scripts {
<script src="~/lib/leaflet/leaflet.min.js"></script>
<script src="~/js/map.js" asp-append-version="true"></script>
}
در این مورد، bootstrap-reboot.min.css
تنظیمات سبک پیشفرض را بازنشانی میکند و leaflet.min.js
مسئول رندر کردن نقشه است، یعنی جمعآوری قطعات از کاشیها در یک نقشه. بیایید ارتفاع بلوک نقشه را به ارتفاع کامل ناحیه قابل مشاهده در فایل map.css
تنظیم کنیم:
#map {
min-height: 100vh;
}
محتوای فایل map.js
نیز بسیار ساده است، اما کمی جالبتر:
var map = L.map('map').setView([47.59995, 19.36623], 13);
const tiles = L.tileLayer('/tiles/{z}/{x}/{y}.png', {
maxZoom: 19,
minZoom: 11
}).addTo(map);
در اینجا ما از API کتابخانه leaflet استفاده میکنیم، جایی که شناسه بلوک نقشه 'map'
را مشخص میکنیم، سپس در روش setView
مختصات مکانی را تنظیم میکنیم که بارگیری اولیه نقشه از آنجا شروع شود و همچنین مقیاس، به عنوان مثال 13. توجه داشته باشید که روش tileLayer
یک الگوی رشته برای درخواست کاشی به سرور را میپذیرد. این آدرس میتواند مطلق برای دسترسی به سرورهای کاشی شخص ثالث یا نسبی باشد همانطور که در مورد ما است. مسیر /tiles/{z}/{x}/{y}.png
هنوز توسط ما پیادهسازی نشده است و این مهمترین بخش روایت ما است که هنوز باید آن را پیادهسازی کنیم.
برای پیادهسازی هندلر درخواست برای تولید کاشیها، بیایید ابتدا یک مسیر جداگانه در فایل Program.cs
تعریف کنیم:
app.MapControllerRoute(name: "tiles",
pattern: "tiles/{z}/{x}/{y}.png",
defaults: new { controller = "Tiles", action = "Index" });
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}");
و سپس کنترلر Tiles
را اضافه کنید:
using Microsoft.AspNetCore.Mvc;
using Aspose.GIS;
using Aspose.GIS.VectorLayers;
using Aspose.GIS.Rendering;
using System.IO;
using System.Threading.Tasks;
using Npgsql;
namespace YourProjectName.Controllers
{
public class TilesController : Controller
{
[HttpGet("tiles/{z}/{x}/{y}.png")]
public async Task<IActionResult> Index(int z, int x, int y)
{
// Logic to generate the tile here
return File(...); // Return the image as a file result
}
}
}
خوب، اکنون میتوانیم کد تولید کاشی را در کنترلر Tiles
اضافه کنیم. این یک نمونه کد است:
using Microsoft.AspNetCore.Mvc;
using Aspose.GIS;
using Aspose.GIS.VectorLayers;
using Aspose.GIS.Rendering;
using System.IO;
using System.Threading.Tasks;
using Npgsql;
namespace YourProjectName.Controllers
{
public class TilesController : Controller
{
[HttpGet("tiles/{z}/{x}/{y}.png")]
public async Task<IActionResult> Index(int z, int x, int y)
{
double scale = 1 / Math.Pow(2, z);
double minX = x * scale;
double minY = y * scale;
double maxX = (x + 1) * scale;
double maxY = (y + 1) * scale;
VectorLayer inputLayer;
using (var conn = new NpgsqlConnection("Host=127.0.0.1;Username=gis;Password=password;Database=Hungary"))
{
var builder = new DatabaseDataSourceBuilder();
builder
.FromQuery(query) // Your query here
.GeometryField("way")
.AddAttribute("osm_id", AttributeDataType.Long)
.AddAttribute("addr:housenumber", AttributeDataType.String)
.AddAttribute("building", AttributeDataType.String)
.AddAttribute("name", AttributeDataType.String)
.AddAttribute("source", AttributeDataType.String)
.AddAttribute("admin_level", AttributeDataType.Integer)
.AddAttribute("place", AttributeDataType.String)
.AddAttribute("landuse", AttributeDataType.String)
.AddAttribute("water", AttributeDataType.String);
conn.Open();
inputLayer = await builder.Build().ReadAsync(conn);
}
var map = new Map(256, 256);
map.SpatialReferenceSystem = SpatialReferenceSystem.WebMercator;
map.Extent = new Extent(minX, minY, maxX, maxY, SpatialReferenceSystem.WebMercator);
// Add layers and render the map here
using var pngStream = new MemoryStream();
map.Render(AbstractPath.FromStream(pngStream), Renderers.Png);
pngStream.Seek(0, SeekOrigin.Begin);
return File(pngStream, "image/png");
}
}
}
امیدوارانه توانستهایم ایدهها و تکنیکهای اساسی ساخت نقشه را به شما منتقل کنیم. ما برای آزمایشات شما آرزوی موفقیت داریم.