در قسمت قبل با داده های مکانی آشنا شدیم، داده های مکانی بیانگر موقعیت فیزیکی و شکل اشیاء بر روی مختصات جغرافیایی هستند. بسیاری از بانک های اطلاعاتی از جمله SQL Server از داده های مکانی پشتیبانی می کنند و می توانیم در کنار سایر انواع داده ای، از داده های مکانی نیز در موجودیت های خودمان استفاده کنیم.
به صورت پیشفرض داده های مکانی در EFCore پشتیبانی نمی شوند مایکروسافت از نسخه EFCore 2.2 کتابخانه NetTopologySuite که به اختصار NTS هم گفته می شود را برای پشتیبانی از داده های مکانی ارائه کرد. پس برای استفاده از داده های مکانی در EFCore حتما باید از نسخه 2.2 به بالاتر استفاده کنیم و کتابخانه NetTopologySuite هم نصب کنیم
- نصب NetTopologySuite
بسته کتابخانه NetTopologySuite را می توانیم از Nuget نصب کنیم. این کتابخانه چهار نوع مختلف را ارائه کرده است که می توانیم بسته به Provider مورد نظرمان یکی از آنها را نصب کنیم.
توضیحات | بسته Nuget | Ef Core Provider |
پشتیبانی از MSSQL Server در Entity Freamwork Core |
| Microsoft.EntityFrameworkCore.SqlServer |
پشتیبانی از پایگاه داده SQLite در Entity Freamwork Core | Microsoft.EntityFrameworkCore.Sqlite | |
NetTopologySuite | Microsoft.EntityFrameworkCore.InMemory | |
پشتیبانی از Npgsql در Entity Freamwork PostgreSQL | Npgsql.EntityFrameworkCore.PostgreSQL |
انواع داده مکانی در NetTopologySuite
شش نوع داده مکانی در کتابخانه NetTopologySuite پشتیبانی می شود که می توانیم از آنها در موجودیت های برنامه استفاده کنیم. انواع داده ای NetTopologySuite در فضای نام NetTopologySuite.Geometries قرار دارند.
- Point
- LineString
- Polygon
- MultiPoint
- MultiLineString
- MultiPolygon
توجه: در قسمت اول مقاله توضیح کاملی در مورد انواع داده های مکانی ارائه شده است.
فعال کردن NetTopologySuite در DBContext
همانطور که گفتیم NetTopologySuite یک کتابخانه مکانی برای فریمورک دات نت است که کار Mapping داده های مکانی در EntityFreamCore را انجام می دهد.
برای فعال کردن mapping انوع داده ای NetTopologySuite به داده های مکانی باید در DBContxt متُد UseNetTopologySuite را در قسمت provider مربوطه استفاده کنیم. به عنوان مثال در SqlServer به صورت زیر آن را فعال می کنیم.
string connection = "Data Source=.;Initial Catalog=BugetoSpatialData;Integrated Security=True;MultipleActiveResultSets=true ";
services.AddEntityFrameworkSqlServer()
.AddDbContext<DataBaseContext>(option =>
option.UseSqlServer(connection, x => x.UseNetTopologySuite()));
استفاده از NetTopologySuite در موجودیت ها
یک کلاس به نام Store ایجاد کرده ایم، از این کلاس برای نگهداری اطلاعات فروشگاه استفاده می کنیم. در اطلاعات فروشگاه ما قصد داریم که موقعیت مکانی فروشگاه هم در کنار دیگر اطلاعات آن نگهداری کنیم. برای نگهداری موقعیت مکانی یک فیلد با نام Location و از نوع Point به کلاس Store اضافه کرده ایم.
برای نمایش یک موقعیت مکانی به دو عدد X,Y و یا همان Lat,Long نیاز داریم، نوع داده ای Point هم دو عدد X,Y را دریافت می کند. که X برای نگهداری Long و Y برای نگهداری Lat است. به همین دلیل برای نگهداری موقعیت مکانی فروشگاه از نوع داده ای Point استفاده کردیم. البته اگر هر فروشگاه چندین شعبه داشته باشد باید از نوع داده ای MultiPoint استفاده کنیم.
using NetTopologySuite.Geometries;
public class Store
{
public long Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
[Column(TypeName = "geometry")]
public Point Location { get; set; }
}
افزودن مقادیر
برای ذخیره مکان یک شئ در بانک اطلاعاتی باید یک نمونه از نوع geometry با استفاده از طول عرض جغرافیایی آن شئ ایجاد کنیم و سپس این نمونه ساخته شده از نوع geometry را ذخیره نماییم. برای ایجاد نمونهgeometry می توانیم از متُد CreateGeometryFactory مربوط به کتابخانه NetTopologySuite استفاده کنیم. پس از ساخت یک نمونه از CreateGeometryFactory می توانیم با استفاده از تابع CreatePoint یک نقطه را ایجاد کنیم. این تابع طول عرض جغرافیایی را به عنوان پارامتر دریافت می کند.
متُد CreateGeometryFactory پارامتری به نام SRID دریافت می کند. SRID شناسه منحصر به فردی است که در سیستم های مختصات جغرافیایی استفاده می شود و عدد 4326 یک استاندارد متداول است که بسیاری از برنامه ها از جمله، Waze ، Google Map از آن برای SRID استفاده کرده اند.
1. برای ایجاد یک Point از کد زیر استفاده می کنیم:
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var currentLocation = geometryFactory.CreatePoint(new NetTopologySuite.Geometries.Coordinate(5,3));
2. برای ایجاد یک LineString از کد زیر استفاده می کنیم:
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
Coordinate[] LineStringArray = new Coordinate[3];
LineStringArray[0] = new Coordinate(51.242079734802246, 35.63340706937167);
LineStringArray[1] = new Coordinate(51.23916149139404, 35.63082591309715);
LineStringArray[2] = new Coordinate(51.23504161834717, 35.633720988098574);
var LineString = geometryFactory.CreateLineString(LineStringArray);
3. برای ایجاد Poygon از کد زیر استفاده می کنیم:
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
Coordinate[] polygonArray = new Coordinate[5];
polygonArray[0] = new Coordinate(51.394686698913574, 35.718608138232895);
polygonArray[1] = new Coordinate(51.392154693603516, 35.71808549588366);
polygonArray[2] = new Coordinate(51.39271259307861, 35.715507076789464);
polygonArray[3] = new Coordinate(51.395244598388665, 35.71655239188316);
polygonArray[4] = new Coordinate(51.394686698913574, 35.718608138232895);
var polygone = geometryFactory.CreatePolygon(polygonArray);
کد زیر افزودن یک فروشگاه به همراه موقعیت مکانی آن به مدل Store را انجام می دهد.
var geometryFactory = NtsGeometryServices.Instance.CreateGeometryFactory(srid: 4326);
var currentLocation = geometryFactory.CreatePoint(new NetTopologySuite.Geometries.Coordinate(51.258741, 36.254155));
Store newStore = new Store()
{
Name = "فروشگاه شماره یک",
Address = "تهران - میدان تیموری - خیابان قاسمی - پلاک 7",
PhoneNumber="09128698172",
Location = currentLocation,
};
_context.Stores.Add(newStore);
_context.SaveChanges();
کوئری گرفتن از داده های مکانی
پس از نصب NetTopologySuite دستورات LINQ این کتابخانه برای استفاده دردسترس ما قرار دارند. این دستورات استفاده از داده های مکانی را برای ما بسیار راحت کرده و بسیاری از کوئری های پیچیده را می توانیم با این دستورات به سادگی اجرا کنیم.
برای مثال اگر ما بخواهیم نزدیکترین فروشگاه به یک موقعیت مکانی را بدست بیاریم از متُد Distance استفاده می کنیم.
var neareStore = db.Stores .OrderBy(c => c.Location.Distance(currentLocation))FirstOrDefault();
داده های مکانی در SQLServer
اگراز پایگاه داده SQLServer استفاده می کنید باید به این مورد توجه داشته باشید. به صورت پیش فرض در SQLServer نوع داده ای، داده های مکانی geography در نظر گرفته می شود.
برای تغییر نوع داده ای به geometry باید یکی از روش های زیر را اعمال کنید.
1. روش استفاده از Data Annotation
[Column(TypeName = "geometry")]
public Point Location { get; set; }
2. روش استفاده از Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Store>(p =>
{
p.Property(store => store.Location).HasColumnType("geometry");
{
}
});
}
امکانات NetTopologySuite
در جدول زیر متُدها و خصوصیات کتابخانه NetTopologySuite را مشاهده می کنیم و مشخص می کنیم که در کدام Provider های مربوط به EFCore پشتیبانی می شود.
Npgsql | SQLite | SQL Server (geography) | SQL Server (geometry) | متد |
✔ | ✔ | ✔ | ✔ | Geometry.Area |
✔ | ✔ | ✔ | ✔ | Geometry.AsBinary |
✔ | ✔ | ✔ | ✔ | Geometry.AsText |
✔ | ✔ | ✔ | Geometry.Boundary | |
✔ | ✔ | ✔ | ✔ | Geometry.Buffer(double) |
✔ | ✔ | Geometry.Buffer(double, int) | ||
✔ | ✔ | ✔ | Geometry.Centroid | |
✔ | ✔ | ✔ | ✔ | Geometry.Contains(Geometry) |
✔ | ✔ | ✔ | ✔ | Geometry.ConvexHull() |
✔ | ✔ | Geometry.CoveredBy(Geometry) | ||
✔ | ✔ | Geometry.Covers(Geometry) | ||
✔ | ✔ | ✔ | Geometry.Crosses(Geometry) | |
✔ | ✔ | ✔ | ✔ | Geometry.Difference(Geometry) |
✔ | ✔ | ✔ | ✔ | Geometry.Dimension |
✔ | ✔ | ✔ | ✔ | Geometry.Disjoint(Geometry) |
✔ | ✔ | ✔ | ✔ | Geometry.Distance(Geometry) |
✔ | ✔ | ✔ | Geometry.Envelope | |
✔ | Geometry.EqualsExact(Geometry) | |||
✔ | ✔ | ✔ | ✔ | Geometry.EqualsTopologically(Geometry) |
✔ | ✔ | ✔ | ✔ | Geometry.GeometryType |
✔ | ✔ | ✔ | Geometry.GetGeometryN(int) | |
✔ | ✔ | ✔ | Geometry.InteriorPoint | |
✔ | ✔ | ✔ | ✔ | Geometry.Intersection(Geometry) |
✔ | ✔ | ✔ | ✔ | Geometry.Intersects(Geometry) |
✔ | ✔ | ✔ | ✔ | Geometry.IsEmpty |
✔ | ✔ | ✔ | Geometry.IsSimple | |
✔ | ✔ | ✔ | Geometry.IsValid | |
✔ | ✔ | ✔ | ✔ | Geometry.IsWithinDistance(Geometry, double) |
✔ | ✔ | ✔ | Geometry.Length | |
✔ | ✔ | ✔ | ✔ | Geometry.NumGeometries |
✔ | ✔ | ✔ | ✔ | Geometry.NumPoints |
✔ | ✔ | ✔ | ✔ | Geometry.OgcGeometryType |
✔ | ✔ | ✔ | ✔ | Geometry.Overlaps(Geometry) |
✔ | ✔ | ✔ | Geometry.PointOnSurface | |
✔ | ✔ | ✔ | Geometry.Relate(Geometry, string) | |
✔ | ✔ | Geometry.Reverse() | ||
✔ | ✔ | ✔ | ✔ | Geometry.SRID |
✔ | ✔ | ✔ | ✔ | Geometry.SymmetricDifference(Geometry) |
✔ | ✔ | ✔ | ✔ | Geometry.ToBinary() |
✔ | ✔ | ✔ | ✔ | Geometry.ToText() |
✔ | ✔ | ✔ | Geometry.Touches(Geometry) | |
✔ | ✔ | Geometry.Union() | ||
✔ | ✔ | ✔ | ✔ | Geometry.Union(Geometry) |
✔ | ✔ | ✔ | ✔ | Geometry.Within(Geometry) |
✔ | ✔ | ✔ | ✔ | GeometryCollection.Count |
✔ | ✔ | ✔ | ✔ | GeometryCollection[int] |
✔ | ✔ | ✔ | ✔ | LineString.Count |
✔ | ✔ | ✔ | ✔ | LineString.EndPoint |
✔ | ✔ | ✔ | ✔ | LineString.GetPointN(int) |
✔ | ✔ | ✔ | LineString.IsClosed | |
✔ | ✔ | ✔ | ✔ | LineString.IsRing |
✔ | ✔ | ✔ | ✔ | LineString.StartPoint |
✔ | ✔ | ✔ | ✔ | MultiLineString.IsClosed |
✔ | ✔ | ✔ | ✔ | Point.M |
✔ | ✔ | ✔ | ✔ | Point.X |
✔ | ✔ | ✔ | ✔ | Point.Y |
✔ | ✔ | ✔ | ✔ | Point.Z |
✔ | ✔ | ✔ | ✔ | Polygon.ExteriorRing |
✔ | ✔ | ✔ | ✔ | Polygon.GetInteriorRingN(int) |
برای افزودن دیدگاه خود، نیاز است ابتدا وارد حساب کاربریتان شوید