Estoy tratando de hacer una consulta donde puedo encontrar todas las ubicaciones en mi base de datos SQL dentro de (digamos 50 millas) de un determinado punto de longitud y latitud.

Rellené algunas publicaciones, y en la tabla Publicar tengo dos columnas donde almaceno Longitud y Latitud al crear una publicación. Así es como se ve la tabla:

My SQL Server 2012 Post Table

He examinado muchos ejemplos de cómo intentar implementar esto, pero parece que simplemente no funcionan. Sé por investigación que ASP.NET Core no es compatible con DbGeography, y otros artículos simplemente están desactualizados.

Encontré esto artículo, pero utiliza DbGeography, por lo que no funciona.

Encontré esto en GitHub: aquí No sé si eso funcionaría.

Solo como referencia, así es como se ve mi modelo Post:

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }

    public double Lat { get; set; }
    public double Lng { get; set; }

    public DateTime Created { get; set; }
    public Category Category { get; set; }
    public int CategoryId { get; set; }

    // Other navigation properties...
}

Y así es como hago mi consulta en este momento para mostrar puntos en mi Google Maps en la interfaz:

public class HomeController : Controller
{
    private readonly ApplicationDbContext _context;

    public HomeController(ApplicationDbContext context)
    {
        _context = context;
    }


    [HttpGet]
    public JsonResult GetGalleryLocations()
    {

        var data = _context.Posts
            .Include(c => c.Category)            
            .ToList();

        return Json(data);
    }       
}

¿Alguien tiene alguna idea sobre cómo implementar una consulta como esta?

1
David 6 mar. 2018 a las 22:32

3 respuestas

La mejor respuesta

¿Qué tal si extrae los datos de la base de datos que se ajustan en el cuadrado alrededor de su círculo de distancia deseada, y luego ajusta en el lado del cliente solo las publicaciones en el círculo?

Dados sus datos iniciales

var myLat = 25.05;
var myLon = -80.3;
var radiusInMile = 50;

Puedes calcular los límites del cuadrado que contiene ese círculo

var minMilePerLat = 68.703;
var milePerLon = Math.Cos(myLat) * 69.172;
var minLat = myLat - radiusInMile / minMilePerLat;
var maxLat = myLat + radiusInMile / minMilePerLat;
var minLon = myLon - radiusInMile / milePerLon;
var maxLon = myLon + radiusInMile / milePerLon;

Luego, puede consultar en la base de datos esas publicaciones dentro del cuadrado, llevarlas al cliente y mantener solo las que están en el círculo

var data = _context.Posts
                   .Where(p => (minLat <= p.Lat && p.Lat <= maxLat) && (minLon <= p.Lng && p.Lng <= maxLon))
                   .AsEnumerable()
                   .Select(p => new { p, Dist = distanceInMiles(myLon, myLat, p.Lng, p.Lat) })
                   .Where(p => p.Dist <= radiusInMile);

Donde la función distanceInMiles se define como

public double ToRadians(double degrees) => degrees * Math.PI / 180.0;
public double distanceInMiles(double lon1d, double lat1d, double lon2d, double lat2d) {
    var lon1 = ToRadians(lon1d);
    var lat1 = ToRadians(lat1d);
    var lon2 = ToRadians(lon2d);
    var lat2 = ToRadians(lat2d);

    var deltaLon = lon2 - lon1;
    var c = Math.Acos(Math.Sin(lat1) * Math.Sin(lat2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(deltaLon));
    var earthRadius = 3958.76;
    var distInMiles = earthRadius * c;

    return distInMiles;
}

Estoy usando la ley esférica de los cosenos para calcular la distancia, que supongo que es suficiente para este problema. Si necesita más precisión, puede actualizar la fórmula a algo más complicado como haversine o Vincenty.

4
NetMage 6 mar. 2018 a las 21:36

Hay una falla con la respuesta NetMage's.

Para Longitud / Latitud negativa, el maxLat / maxLon será menor que su minLat / minLon y su consulta se verá fuera del cuadro delimitador.

Esta es una de las formas correctas de calcular el cuadro delimitador en función de su respuesta (de esta manera, se asegura de que el mínimo siempre sea inferior al máximo)

var minMilePerLat = 68.703;
var milePerLon = Math.Cos(myLat) * 69.172;
var minLat = Math.min(myLat - radiusInMile / minMilePerLat, myLat + radiusInMile / minMilePerLat);
var maxLat = Math.max(myLat - radiusInMile / minMilePerLat, myLat + radiusInMile / minMilePerLat);
var minLon = Math.min(myLon - radiusInMile / milePerLon, myLon + radiusInMile / milePerLon);
var maxLon = Math.max(myLon - radiusInMile / milePerLon, myLon + radiusInMile / milePerLon);

También puede cambiar las cosas cuando realiza consultas en función de min y max, pero pensé que esto es más simple.

0
Aswin Ramakrishnan 9 feb. 2019 a las 23:39

Introdujeron el soporte de datos espaciales con la versión 2.2 de Entity Framework Core.

Puede echar un vistazo a este StackOverflow respuesta.

1
Marko Papic 10 dic. 2018 a las 09:18