Então, usando a sugestão de Phil, eu reescrevi muito disso usando espaço. Isso funcionou muito bem, você só precisa de .NET4.5, EF5+ e sql server (2008 e acima eu acredito). Estou usando EF6 + Sql Server 2012.
Configuração
O primeiro passo foi adicionar um
Geography
coluna para meu banco de dados EventListings
tabela (clique com o botão direito sobre ela -> Design). Eu nomeei minha localização:Em seguida, como estou usando o EDM com banco de dados primeiro, tive que atualizar meu modelo para usar o novo campo que criei. Então recebi um erro sobre não conseguir converter Geography para double, então o que fiz para corrigir foi selecionar a propriedade Location na entidade, ir até suas propriedades e alterar seu tipo de double para
Geography
:Consultar dentro de um raio e adicionar eventos com locais
Então tudo o que você precisa fazer é consultar sua coleção de entidades assim:
var events = EventRepository.EventListings
.Where(x => x.Location.Distance(originCoordinates) * 0.00062 <= radiusParam);
O método de extensão Distance obtém a distância do objeto atual até o "outro" objeto que você passa. Este outro objeto é do tipo
DbGeography
. Você acabou de chamar um método estático e ele cria um desses filhotes, então apenas jogue sua Longitude e Latitude nele como um ponto:DBGeography originCoordinates = DBGeography.fromText("Point(" + originLongitude + " " + originLatitude + ")");
Não foi assim que criei meu originCoordinates. Baixei um banco de dados separado que tinha uma lista de todos os CEPs e suas Latitudes e Longitudes. Adicionei uma coluna do tipo
Geography
a isso também. Vou mostrar como no final desta resposta. Em seguida, consultei o contexto do CEP para obter um objeto DbGeography do campo Location na tabela ZipCode. Se o usuário quiser uma origem mais específica do que apenas um CEP, eu faço uma chamada para a API do Google Maps (GeoCode para ser mais específico) e obtenho a latitude e longitude para o endereço específico do usuário por meio de um webservice, e crio um objeto DBGeography de os valores de Latitude e Longitude na resposta.
Eu uso APIs do google quando crio um evento também. Acabei de definir a variável de localização assim antes de adicionar minha entidade ao EF:
someEventEntity.Location = DBGeography.fromText("Point(" + longitudeFromGoogle+ " " + latitudeFromGoogle + ")");
Outras dicas, truques e solução de problemas
Como consegui o método de extensão de distância?
Para obter o método de extensão
Distance
você deve adicionar uma referência ao seu projeto:System.Data.Entity
Depois de fazer isso, você deve adicionar o using:using System.Data.Entity.Spatial;
para sua classe. A
Distance
método de extensão retorna a distância com uma unidade de medida de metros (Você pode alterá-lo, eu acho, mas isso é padrão). Aqui, meu parâmetro de raio estava em milhas, então fiz algumas contas para converter. Observação :Existe uma
DBGeography
classe em System.Data.Spatial
. Este é o errado e não funcionaria para mim. Muitos exemplos que encontrei na internet usaram este. Como converter latitude/longitude em coluna de geografia
Então, se você fosse como eu e baixasse um banco de dados de CEP com todos os
Latitude
&Longitude
colunas, e então percebi que não tinha uma coluna Geografia... isso pode ajudar:1) Adicione uma coluna Local à sua tabela. Defina o tipo como Geografia.
2) Execute o seguinte sql
UPDATE [ZipCodes]
SET Location = geography::STPointFromText('POINT(' + CAST([Longitude] AS VARCHAR(20)) + ' ' + CAST([Latitude] AS VARCHAR(20)) + ')', 4326)
Como visualizar os detalhes de um item de geografia
Portanto, ao consultar sua tabela EventListings no Sql Server Management Studio depois de inserir alguns itens DbGeography, você verá o
Location
coluna contém um valor hexadecimal como:0x1234513462346. Isso não é muito útil quando você quer ter certeza de que os valores corretos foram inseridos. Para realmente visualizar a latitude e longitude deste campo, você deve consultá-lo assim:
SELECT Location.Lat Latitude, Location.Long Longitude
FROM [EventListings]