Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Como uso o espaço para pesquisar um raio de CEPs?


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]