laitimes

Asp.Net Core MongoDB spatial positioning (point) and distance retrieval

author:CShap New Force

One: Let's prepare some data first

Picking points using the Gaode coordinate system (https://lbs.amap.com/tools/picker)

Note: To register as a developer, otherwise the latitude and longitude picked up will only be accurate to 2 digits

第一百货 119.647437,29.083693
欧景名城 119.639798,29.080843
金华网络经济中心 119.644604,29.074617
金华市第十五中学 119.652758,29.075029
金磐医院 119.642716,29.072554
龙腾江南建材市场 119.636579,29.072366
宝莲广场 119.644433,29.079568
新世纪大厦 119.655548,29.08088
泰地世璟园 119.658809,29.073379
美兰湖公馆 119.639497,29.06824
西京大厦 119.63645,29.081855
星月花园 119.635592,29.078292
兰溪街商业步行街 119.649325,29.082606
阳光城市花园 119.666963,29.082456
华园小区 119.666491,29.073604
印象城 119.658509,29.070716           
Asp.Net Core MongoDB spatial positioning (point) and distance retrieval

geographical location

Two: Save the data to MongoDB for easy use later

db.mapinfo.insert({"address": "第一百货", "name":"第一百货", "location": {"type": "Point", "coordinates": [119.647437,29.083693]}})
db.mapinfo.insert({"address": "欧景名城", "name":"欧景名城", "location": {"type": "Point", "coordinates": [119.639798,29.080843]}})
db.mapinfo.insert({"address": "金华网络经济中心", "name":"金华网络经济中心", "location": {"type": "Point", "coordinates": [119.644604,29.074617]}})
db.mapinfo.insert({"address": "金华市第十五中学", "name":"金华市第十五中学", "location": {"type": "Point", "coordinates": [119.652758,29.075029]}})
db.mapinfo.insert({"address": "金磐医院", "name":"金磐医院", "location": {"type": "Point", "coordinates": [119.642716,29.072554]}})
db.mapinfo.insert({"address": "龙腾江南建材市场", "name":"龙腾江南建材市场", "location": {"type": "Point", "coordinates": [119.636579,29.072366]}})
db.mapinfo.insert({"address": "宝莲广场", "name":"宝莲广场", "location": {"type": "Point", "coordinates": [119.644433,29.079568]}})
db.mapinfo.insert({"address": "新世纪大厦", "name":"新世纪大厦", "location": {"type": "Point", "coordinates": [119.655548,29.08088]}})
db.mapinfo.insert({"address": "泰地世璟园", "name":"泰地世璟园", "location": {"type": "Point", "coordinates": [119.658809,29.073379]}})
db.mapinfo.insert({"address": "美兰湖公馆", "name":"美兰湖公馆", "location": {"type": "Point", "coordinates": [119.639497,29.06824]}})
db.mapinfo.insert({"address": "西京大厦", "name":"西京大厦", "location": {"type": "Point", "coordinates": [119.63645,29.081855]}})
db.mapinfo.insert({"address": "星月花园", "name":"星月花园", "location": {"type": "Point", "coordinates": [119.635592,29.078292]}})
db.mapinfo.insert({"address": "兰溪街商业步行街", "name":"兰溪街商业步行街", "location": {"type": "Point", "coordinates": [119.649325,29.082606]}})
db.mapinfo.insert({"address": "阳光城市花园", "name":"阳光城市花园", "location": {"type": "Point", "coordinates": [119.666963,29.082456]}})
db.mapinfo.insert({"address": "华园小区", "name":"华园小区", "location": {"type": "Point", "coordinates": [119.666491,29.073604]}})
db.mapinfo.insert({"address": "印象城", "name":"印象城", "location": {"type": "Point", "coordinates": [119.658509,29.070716]}})           

Be sure to index it

MongoDB provides two types of map indexes, 2D and 2Dsphere.

2D indexing records point coordinates through a two-dimensional plane, supports calculating distances in planar geometry, while 2Dsphere supports distance calculations on spherical surfaces, and supports all of MongoDB's geospatial query methods.

Simply understood, 2Dsphere is an enhanced version of 2D. According to the official recommendation, if your MongoDB version is greater than 2.6

db.mapinfo.ensureIndex({location : "2dsphere"})           
Asp.Net Core MongoDB spatial positioning (point) and distance retrieval

Database data

Suppose the current location is in the "1921 Cultural and Creative Park" Specific coordinates 119.627438,29.078988

Asp.Net Core MongoDB spatial positioning (point) and distance retrieval

Current location

Retrieve data within a specified radius in meters

Take 2000 meters here

db.mapinfo.find({location: {$near: {$geometry: {type: "Point", coordinates: [119.627438,29.078988]}, $maxDistance: 2000}}})           

or

db.mapinfo.find({location: {$nearSphere: {$geometry: {type: "Point", coordinates: [119.627438,29.078988]}, $maxDistance: 2000}}})           
Asp.Net Core MongoDB spatial positioning (point) and distance retrieval

Query results, from near to far

In order closest to me, in addition to using $nearSphere queries, we can also use aggregate to achieve this.

There are two benefits to using aggregates.

1. After we sort it, we can return the distance between two points.

2. We can do more complex sorting, for example, we can sort according to a certain field first, and then sort according to distance if the field is the same.

db.mapinfo.aggregate([
   {
     $geoNear: {
        near: {type: "Point", coordinates: [119.627438,29.078988]},
        distanceField: "distance",
        spherical: true,       
        maxDistance: 15000,
				query: {
			    address: {$regex:'建材'}
				}
     }
   },
   { $skip: 0 },
   { $limit: 2 }
])           
Asp.Net Core MongoDB spatial positioning (point) and distance retrieval

aggregate queries

If we want to query data within a few kilometers of a circle centered on a certain point, you can do the following:

The distance of 1 arc on the earth's surface is about 6378137 meters, and the distance of 0.001 radians is 6378 meters

db.mapinfo.find({
    "location": {
        "$geoWithin": {
            "$centerSphere": [
                [
                    119.627438,29.078988
                ],
                0.025 // 单位为弧度
            ]
        }
    }
})           
Asp.Net Core MongoDB spatial positioning (point) and distance retrieval

Data within a few kilometers of a circle centered on a point

Writing here, in fact, based on MongoDB spatial positioning (point) and distance retrieval has been realized

How the code in .NET is implemented is explained below.

Write the entity object mapinfo

public class mapinfo
    {
        /// <summary>
        /// 主键
        /// </summary>
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string id { get; set; }

        /// <summary>
        /// 地址
        /// </summary>
        public string address { get; set; }
        /// <summary>
        /// 社区名称
        /// </summary>
        public string name { get; set; }

        /// <summary>
        /// 距离
        /// </summary>
        public double? distance { get; set; }

        /// <summary>
        /// 经纬度坐标
        /// </summary>
        public GeoJsonPoint<GeoJson2DGeographicCoordinates> location { get; set; }
    }           

IMapinfoMongoRepository

public interface IMapinfoMongoRepository
    {
        public List<Models.MongoEnity.mapinfo> QueryNear(string keywords,int pageIndex, int pageSize, double longitude, double latitude, int maxDistance);
    }           

MapinfoMongoRepository

public class MapinfoMongoRepository : IMapinfoMongoRepository
    {
        private readonly ILogger<MapinfoMongoRepository> _logger;
        private readonly IMongoDbGenericHelper<Models.MongoEnity.mapinfo> _mapinfoHelper;
        public MapinfoMongoRepository(ILogger<MapinfoMongoRepository> logger, IMongoDbGenericHelper<Models.MongoEnity.mapinfo> mapinfoHelper)
        {
            _logger = logger;
            _mapinfoHelper = mapinfoHelper;
        }

        /// <summary>
        /// 查询附近的信息
        /// </summary>
        /// <param name="keywords"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="longitude"></param>
        /// <param name="latitude"></param>
        /// <param name="maxDistance"></param>
        /// <returns></returns>
        public List<Models.MongoEnity.mapinfo> QueryNear(string keywords, int pageIndex, int pageSize, double longitude, double latitude, int maxDistance)
        {
   
            IList<IPipelineStageDefinition> stages = new List<IPipelineStageDefinition>();
            string temp1 = "";
            if (!string.IsNullOrEmpty(keywords))
            {
                temp1 = "{\"$geoNear\": {\"near\": {\"type\": \"Point\",\"coordinates\": [" + longitude + "," + latitude + "]},\"distanceField\": \"distance\",\"spherical\": true,\"maxDistance\": " + maxDistance + ",query:{\"address\": { $regex: '" + keywords + "' }}}}";
            }
            else
            {
                temp1 = "{\"$geoNear\": {\"near\": {\"type\": \"Point\",\"coordinates\": [" + longitude + "," + latitude + "]},\"distanceField\": \"distance\",\"spherical\": true,\"maxDistance\": " + maxDistance + "}}";
            }

            PipelineStageDefinition<Models.MongoEnity.mapinfo, Models.MongoEnity.mapinfo> stage1 = new JsonPipelineStageDefinition<Models.MongoEnity.mapinfo, Models.MongoEnity.mapinfo>(temp1);
            stages.Add(stage1);

            string temp2 = "{$skip:" + (pageIndex - 1) * pageSize + "}";
            PipelineStageDefinition<Models.MongoEnity.mapinfo, Models.MongoEnity.mapinfo> stage2 = new JsonPipelineStageDefinition<Models.MongoEnity.mapinfo, Models.MongoEnity.mapinfo>(temp2);
            stages.Add(stage2);

            string temp3 = "{$limit:" + pageSize + "}";
            PipelineStageDefinition<Models.MongoEnity.mapinfo, Models.MongoEnity.mapinfo> stage3 = new JsonPipelineStageDefinition<Models.MongoEnity.mapinfo, Models.MongoEnity.mapinfo>(temp3);
            stages.Add(stage3);

            PipelineDefinition<Models.MongoEnity.mapinfo, Models.MongoEnity.mapinfo> pipeline = new PipelineStagePipelineDefinition<Models.MongoEnity.mapinfo, Models.MongoEnity.mapinfo>(stages);

            //聚合结果
            List<Models.MongoEnity.mapinfo> result = _mapinfoHelper.GetCollection(nameof(Models.MongoEnity.mapinfo)).Aggregate(pipeline).ToList();

            return result;
        }
    }           

Action

[HttpGet]
        public IActionResult TestQueryNear()
        {
            var data = _mapinfoMongoRepository.QueryNear(keywords: string.Empty, pageIndex: 1, pageSize: 2, longitude: 119.627438, latitude: 29.078988, maxDistance: 15000);
            return Ok(data);
        }           

Query results:

Asp.Net Core MongoDB spatial positioning (point) and distance retrieval

Query results

Query results The distance unit is meters

[
  {
    "id": "64ae31fe6412e519c80f4b3d",
    "address": "星月花园",
    "name": "星月花园",
    "distance": 797.0567583679608,
    "location": {
      "Coordinates": {
        "Values": [
          119.635592,
          29.078292
        ],
        "Longitude": 119.635592,
        "Latitude": 29.078292
      },
      "Type": 7,
      "BoundingBox": null,
      "CoordinateReferenceSystem": null,
      "ExtraMembers": null
    }
  },
  {
    "id": "64ae31fe6412e519c80f4b3c",
    "address": "西京大厦",
    "name": "西京大厦",
    "distance": 933.0219480837998,
    "location": {
      "Coordinates": {
        "Values": [
          119.63645,
          29.081855
        ],
        "Longitude": 119.63645,
        "Latitude": 29.081855
      },
      "Type": 7,
      "BoundingBox": null,
      "CoordinateReferenceSystem": null,
      "ExtraMembers": null
    }
  }
]           

Knowledge is not easy to organize, like friends Guanzhu