スプリングブート


O Obejetivo desseポストCriar Nua Aplicaは、o GeoLocalizaは、o o、dizer quais pontos est poo o pr o xmos de voc - stro - porエプシ・イ・スプー・プノ・プルー・プルー・プルー・プルー・プルー・セク・プ・ヴォーク・アヌ・エププレッサe Enteras e Preisa Saber Qual fornecedor Est - Lol Avis Prパラアイスス・ウスレス・アキー・スプリング・ブート、ポイスTEM TODA UMA FacilidadeパラCria - Elaze Do O PropJe e Ma Comunidade E Documenta Les Hen - es Muito Ativas、MongoDB Al Me M de Ser - Ma - Boa - al - m - d - ser - Ser - Be op - e - de - por - Toda
AQUA FREERES Sは、oをバックエンドにします.

クリダスAは、スプリングブーツ


パラシュートで降下するSpring Initalizr , Entrando Na Pは、gicieso o o proarのprocito、aqui eu irei usar o春ウェブパラシュートで降下している人のポルノFerzer Lonbok e Spre m m Estou usando Lombok e春の春のDevool Mas s Spro o o mis pela facilidade que o lombok fornece quadmos criarmos os nossos pojos e o o devtools para podermos usar em desenvolvito e termos o Live Reload da Aplica哀愁O .
Fent Mais - ou Menos - assim o projeto :

は、Googleサービスマップ、como estou usando Mavenをします
<!-- https://mvnrepository.com/artifact/com.google.maps/google-maps-services -->
<dependency>
    <groupId>com.google.maps</groupId>
    <artifactId>google-maps-services</artifactId>
    <version>0.11.0</version>
</dependency>

Tamb - m adicione oドライバーは、mongo ao pomをします.
<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
</dependency>

Descriiは、Sexo o o da aplica


Vamos Emular Uma Rede Enregas de Alientos , Ode de Estestelecimentos est Astract o Cadastrados e Quadan um usu Le Digitar o seu endere sée o e e Ele ir

モデル


vamosは、ar ar o nossoモデルcom o que que Seria ent o o o o estelecimento El Possuirにノーム、EメールA sua Localiza Sponrico o ent ent o o vouは、classe localizacaoを得ます.
package com.challenge.geolocation.model;

import java.util.List;

import lombok.Data;

@Data
public class Localizacao {

    private String endereco;
    private List<Double> coordinates;    
    private String type = "Point";

}
Aqui Temos O Endereは、Ma Tamb Les m Temos Dois Atributos que Podem Paecer um Pocco Estranhos O座標E Oタイプです.OSのdois sは、o - necessは、rios Quadan Estamos Trabalhando com GeoLocaliza Spoialhando com GeoLocaliza Spoialhando com GeoLocaliza Spoialhando com GeoLocaliza SoliSolo O com Mongo、o Primeiro Valorは、Um ListaデDouble Contendoに、緯度E E経度E OタイプDeveitoにum pon - no mapa、podemos ter outrosタイプcomo多角形を与えます.
アゴラCriando a nossa classeは、構内にDesticsを所有します.
package com.challenge.geolocation.model;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.mapping.Document;

import lombok.Data;

package com.challenge.geolocation.model;
import org.

import lombok.Data;

@Datapublic class Estabelecimento

    private ObjectId id;

    private String nome;
    private String email;
    private Localizacao localizacao;    

}

Tema classe localizacao e comのosのアトリビュートである.

コーデック


アゴラテモオOモデルクリードMaプリサマーズfazer de alguma forma pra que a nossa aplica .レアentra OSコーデック、El Vai ser o resple vl por fazer tanto o envio como o recebimento dos dojetos do mongo.
<井上>
package com.challenge.geolocation.codec;

import org.bson.BsonReader;
import org.bson.BsonValue;
import org.bson.BsonWriter;
import org.bson.codecs.CollectibleCodec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;

import com.challenge.geolocation.model.Estabelecimento;

public class EstabelecimentoCodec implements CollectibleCodec<Estabelecimento>{

    @Override
    public void encode(BsonWriter writer, Estabelecimento value, EncoderContext encoderContext) {
        // TODO Auto-generated method stub

    }

    @Override
    public Class<Estabelecimento> getEncoderClass() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Estabelecimento decode(BsonReader reader, DecoderContext decoderContext) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Estabelecimento generateIdIfAbsentFromDocument(Estabelecimento document) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean documentHasId(Estabelecimento document) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public BsonValue getDocumentId(Estabelecimento document) {
        // TODO Auto-generated method stub
        return null;
    }

}
アゴラPremiisamesは、Aを持っていますAは、Aを持ってきます.そして、nossa maneira para ELEポーザーfazer o encod o o復号化、パラシュートIsos Vamos adicionar Aは、Pacote BSON que NOS Ajudaをします、Vamos Tipa la como - um文書e vamos adicion
import org.bson.Document;
import org.bson.codecs.Codec;

import com.challenge.geolocation.model.Estabelecimento;


public class EstabelecimentoCodec implements CollectibleCodec<Estabelecimento>{

    private Codec<Document> codec;

    public EstabelecimentoCodec(Codec<Document> codec) {
        this.codec = codec;
    }
アゴラvamosは、o o mのtodo responsを訴えます.Aqui - es - onde dizemos como ser - o o salo os nosos objetos em java parum objeto do mongo
@Override
public void encode(BsonWriter writer, Estabelecimento estabelecimento, EncoderContext encoder) {
    Document document = new Document();

    document.put("_id", estabelecimento.getId());
    document.put("nome", estabelecimento.getNome());
    document.put("email", estabelecimento.getEmail());

    Localizacao localizacao = estabelecimento.getLocalizacao();

    List<Double> coordinates = new ArrayList<>();
    localizacao.getCoordinates().forEach(coordinates::add);

    document.put("localizacao", new Document()
            .append("endereco", localizacao.getEndereco())
            .append("coordinates", coordinates)
            .append("type", localizacao.getType()));

    codec.encode(writer, document, encoder);
}
E Aquiは、ondeを解読します、o como o Java vai解釈者o objeto retornadoは、mongoをします
@Override
public Estabelecimento decode(BsonReader reader, DecoderContext decoderContext) {

    Document document = codec.decode(reader, decoderContext);

    Estabelecimento estabelecimento = new Estabelecimento();
estabelecimento.
    estabelecimento.setNome(document.getString("nome"));
    estabelecimento.setEmail(document.getString("email"));

    Document localizacao = (Document) document.get("localizacao");
    if(localizacao != null) {
        String endereco = localizacao.getString("endereco");
        @SuppressWarnings("unchecked")
        List<Double> coordinates = (List<Double>) localizacao.get("coordinates");

        Localizacao localizacaoEntity = new Localizacao();
        localizacaoEntity.setEndereco(endereco);
        localizacaoEntity.setCoordinates(coordinates);

        estabelecimento.setLocalizacao(localizacaoEntity);
    }

    return estabelecimento;
}
E Temos OS OUTRORS Mを使用しています
@Override
public Class<Estabelecimento> getEncoderClass() {
    return Estabelecimento.class;
}

@Override
public Estabelecimento generateIdIfAbsentFromDocument(Estabelecimento estabelecimento) {
    return documentHasId(estabelecimento) ? estabelecimento.generateId() : estabelecimento;
}

@Override
public boolean documentHasId(Estabelecimento estabelecimento) {
    return estabelecimento.getId() == null;
}

@Override
public BsonValue getDocumentId(Estabelecimento estabelecimento) {
    if (!documentHasId(estabelecimento)) {
        throw new IllegalStateException("This Document do not have a id");
    }

    return new BsonString(estabelecimento.getId().toHexString());
}
NICA Coisa Aqui A ressaltar foi a a a a a a eso o o o do m todo generatimto que Fica Asym :
package com.challenge.geolocation.model;
import org.

import lombok.Data;

@Datapublic class Estabelecimento

    private ObjectId id;

    private String nome;
    private String email;
    private Localizacao localizacao;    

    public Estabelecimento generateId() {
    this
        return this;
    }

}

倉庫


アゴラtemos oモデルe oコーデックアゴラpodemos criar o nossoリポジトリqir ir fafao o acesso ao banco e ficar .
package com.challenge.geolocation.repository;

import org.springframework.stereotype.Repository;

import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;

@Repository
public class EstabelecimentoRepository {

    private MongoClient client;
    private MongoDatabase mongoDataBase;

アクリCrieiは、アッセンブリーエッセンシャルのアオシススプリングqueエサclasseはるかに管理リポジトリque AZ春queエサclasseはるかに管理します.
アゴラVamos abrirは、コネデo o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o
    @Value("${host}")
    private String host;

    @Value("${port}")
    private String port;

    @Value("${database}")
    private String database;

    @Value("${collection.estabelecimento}")
    private String estabelecimento;

    private MongoCollection<Estabelecimento> openConnetion() {
        Codec<Document> codec = MongoClient.getDefaultCodecRegistry().get(Document.class);

        EstabelecimentoCodec estCodec = new EstabelecimentoCodec(codec);

        CodecRegistry registry = CodecRegistries.fromRegistries(MongoClient.getDefaultCodecRegistry(),
                CodecRegistries.fromCodecs(estCodec));

        MongoClientOptions options = MongoClientOptions.builder().codecRegistry(registry).build();

        this.client = new MongoClient(host + ":" + port, options);
        this.mongoDataBase = client.getDatabase(database);

        return this.mongoDataBase.getCollection(this.estabelecimento, Estabelecimento.class);
    }

    private void closeConnection() {
        this.client.close();
    }
fazendo uso da注釈@ valeu春春conseguimos recuperar o valor que est no noアプリケーションyl contendo o host , port , o database e o nome daコレクション.
ENTは、o oコーデックe conectamosなしmongo e j - cesペガモスのaを持っています.
アゴラvamos ao mは、Busca e Agrega Sponistro O Dess Dados SE Basando na Straididadeを好みます.

    public List<Estabelecimento> searchByGeolocation(Filter filter) {
        try {
            MongoCollection<Estabelecimento> estabelecimentoCollection = openConnetion();

            estabelecimentoCollection.createIndex(Indexes.geo2dsphere("localizacao"));

            Point referencePoint = new Point(new Position(filter.getLat(), filter.getLng()));

            MongoCursor<Estabelecimento> resultados = estabelecimentoCollection
                    .find(Filters.nearSphere("localizacao", referencePoint, filter.getDistance(), 0.0)).limit(filter.getLimit()).iterator();

            List<Estabelecimento> estabelecimentos = fillEstabelecimento(resultados);

            return estabelecimentos;
        } finally {
            closeConnection();
        }
    }

    private List<Estabelecimento> fillEstabelecimento(MongoCursor<Estabelecimento> resultados) {
        List<Estabelecimento> estabelecimentos = new ArrayList<>();
        while (resultados.hasNext()) {
            estabelecimentos.add(resultados.next());
        }
        return estabelecimentos;
    }
ENT Ai Ai Arimos : CONPEX CLUE O o COM O M THEO OpenContext () que NOS : NSSAコレクションE como queremos fazer - uma - busca por priididade adicionamum um de nice e dizemos que o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o d o d d dzemos
db.estabelcimento.createIndex({
    localizacao : "2dsphere"
})
イスソ・オ・イェーヌ・オセガス・オゼーサにおける「第一の道」の位置
EM SegiGua Criamos Ma Classse Point queは、緯度E e経度e que serを引きます.コモペガーは、nossa緯度E経度?<研究ノート>前代未聞の説法について
アゴラpodemos dispararノッサpesquisa :
MongoCursor<Estabelecimento> resultados = estabelecimentoCollection
                    .find(Filters.nearSphere("localizacao", referencePoint, filter.getDistance(), 0.0)).limit(filter.getLimit()).iterator();
私は、ノッサBusca、Temosがnossaコレクションe usamos o mをするということであると仮定します.アクアエムMetros、daノッサpesquisa e mは、ニマdistを引きます;Tamb - M m Passamos O Resultue e hamamos O Iterator Per Podermos Percorrers o que Nos Voltar do Mongo
<判例研究>
    private List<Estabelecimento> fillEstabelecimento(MongoCursor<Estabelecimento> resultados) {
        List<Estabelecimento> estabelecimentos = new ArrayList<>();
        while (resultados.hasNext()) {
            estabelecimentos.add(resultados.next());
        }
        return estabelecimentos;
    }

サービス


アゴラは、アッセラFareRemos a a serse de servi es o o、que irの節を実行します.Prip - isso ser - no - se - se - rio - um , n - no - o - se preocupe com isso pois o Google dar - le - um valmeem registre e ap or s isso n so o cobrar - nap sem seu同意書

P . S . S . S . S .

アゴラとしてのアプサEPKEYエムm ' o ' j ' s ' s ' s a ar ar a a a ar ar a a a a a a a a a a ar ar a a a a a a ar ar a a a a ar ar a a a a ar ar a a a a a ar ar a a a a a ar ar a a a a a ar ar a a a a a ar ar a a a a a ar Ar a a a a ar Ar a a
package com.challenge.geolocation.service;

import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.challenge.geolocation.dto.EstabelecimentoDTO;
import com.challenge.geolocation.filter.Filter;
import com.challenge.geolocation.model.Estabelecimento;
import com.challenge.geolocation.repository.EstabelecimentoRepository;
import com.google.maps.GeoApiContext;
import com.google.maps.GeocodingApi;
import com.google.maps.GeocodingApiRequest;
import com.google.maps.errors.ApiException;
import com.google.maps.model.GeocodingResult;
import com.google.maps.model.Geometry;
import com.google.maps.model.LatLng;

@Service
public class EstabelecimentoService {

    @Autowired
    private EstabelecimentoRepository repository;

    @Value("${apikey}")
    private String apikey;

    public List<EstabelecimentoDTO> procuraEstabelecimentosProximoAMim(String endereco, String distance, String limit) {

        GeoApiContext context = new GeoApiContext.Builder().apiKey(apikey).build();

        GeocodingApiRequest request = GeocodingApi.newRequest(context).address(endereco);

        try {
            GeocodingResult[] results = request.await();
            GeocodingResult resultado = results[0];

            Geometry geometry = resultado.geometry;

            LatLng location = geometry.location;

            List<Estabelecimento> estabelecimentoList = repository.searchByGeolocation(
                    Filter.toFilter(location.lat, location.lng, Double.valueOf(distance), Integer.valueOf(limit)));

            List<EstabelecimentoDTO> dtoList = estabelecimentoList.stream().map(estabelecimento -> {
                return EstabelecimentoDTO.toDTO(estabelecimento);
            }).collect(Collectors.toList());

            return dtoList;
        } catch (ApiException | InterruptedException | IOException e) {
            e.printStackTrace();
        }

        return List.of();
    }
}

Criamos o m m to do do do do a a a a a a a a a a a a a a a a a a a erriecoisso por si s s poo ocasionar muitos問題ema too o m o todo do an do a aqui n suo o vamos nos aprofundar tratando .
p ' s o o retornoペガモスo resulto e NavegamosのOvjetoデretornoは、Chearmos One Queremosの位置で、位置que queについて、緯度e e経度eアゴラpodemos chamar o nossoリポジトリque irを実行します.

フィルタ


ponデオブザーバは、Vocは、dec ter notado a classeフィルタe o mをtodo tofilter na nossaサービスe naリポジトリoフィルタcom getlat、getlng、getdistance e getlimit.El Nos ajuda n n o o passar um valor muito grande vari di vis na chamada de um m todo,segue
@Getter
public class Filter {

    private Filter() {}

    private double lat;
    private double lng;
    private double distance;
    private int limit;

    public static Filter toFilter(double latitude, double longitude, double distance, int limit) {
        Filter filter = new Filter();
        filter.lat = latitude;
        filter.lng = longitude;
        filter.distance = distance == 0 ? 1000.0 : distance;
        filter.limit = limit == 0 ? 10 : limit;     

        return filter;
    }

}

コントローラ


アゴラiRemos criarは、nossaコントローラーonde Retberemosを要求します.
package com.challenge.geolocation.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.challenge.geolocation.dto.EstabelecimentoDTO;
import com.challenge.geolocation.service.EstabelecimentoService;

@RestController
@RequestMapping("api")
public class EstabalecimentoController {

    @Autowired
    private EstabelecimentoService service;

    @GetMapping("estabelecimento")
    public List<EstabelecimentoDTO> pegaEstabelecimentosProximosPeloEndereco(
            @RequestParam(name = "limit", defaultValue = "10") String limit,
            @RequestParam(name = "distancia", defaultValue = "1000.00") String distancia,
            @RequestParam("endereco") String endereco) {
        return service.procuraEstabelecimentosProximoAMim(endereco, distancia, limit);
    }

}
Temos Aqui a nossa estalaleMentoController com unm m ' t todo pgaestelecimentosproximospeloderdereco e e OSは、COM um valorデフォルトde 10 caso n - splo o sejaエスペを選びます.
<研究ノート>オーサ・コンソーシアムにおけるNSUSOモデルに関する一考察DO ( Data Transfer Object )のデヴォレヴモスについて
package com.challenge.geolocation.dto;

import com.challenge.geolocation.model.Estabelecimento;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;

import lombok.Getter;

@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
@Getter
public class EstabelecimentoDTO {

    private EstabelecimentoDTO() {}

    private String nome;
    private String email;
    private String endereco;

    public static EstabelecimentoDTO toDTO(Estabelecimento estabelecimento) {
        EstabelecimentoDTO dto = new EstabelecimentoDTO();

        dto.nome = estabelecimento.getNome();
        dto.email = estabelecimento.getEmail();
        dto.endereco = estabelecimento.getLocalizacao().getEndereco();      

        return dto;
    }
}
NICA Responseabilade Desse Dto - Equa One Um Do Per Re Retornado Paria Controller E issoを中心として

決勝戦


Dados , Do Dados , Int , O , O , Podir , InserirのMongoos OS Dados
{
    "nome" : "Mercado I",
    "email" : "[email protected]",
    "localizacao" : {
        "endereco" : "Rua Brigadeiro Tobias n 780",
        "coordinates" : [ 
            -23.53624, 
            -46.63395
        ],
        "type" : "Point"
    }
}

{    
    "nome" : "Estabelecimento II",
    "email" : "[email protected]",
    "localizacao" : {
        "endereco" : "R. Brg. Tobias, 206 - Santa Ifigênia, São Paulo - SP, 01032-000",
        "coordinates" : [ 
            -23.54165, 
            -46.63583
        ],
        "type" : "Point"
    }
}

{    
    "nome" : "Estabelecimento III",
    "email" : "[email protected]",
    "localizacao" : {
        "endereco" : "Av. Cásper Líbero, 42 - Centro Histórico De São Paulo, São Paulo - SP, 01033-000",
        "coordinates" : [ 
            -23.54132, 
            -46.63643
        ],
        "type" : "Point"
    }
}

{    
    "nome" : "Estabelecimento IV",
    "email" : "[email protected]",
    "localizacao" : {
        "endereco" : "Av. Rio Branco, 630 - República, São Paulo - SP, 01205-000",
        "coordinates" : [ 
            -23.53984, 
            -46.64008
        ],
        "type" : "Point"
    }
}

{    
    "nome" : "Estabelecimento V",
    "email" : "[email protected]",
    "localizacao" : {
        "endereco" : "Alameda Barão de Limeira, 425 - Campos Elíseos, São Paulo - SP, 01202-900",
        "coordinates" : [ 
            -23.53386, 
            -46.6482
        ],
        "type" : "Point"
    }
}

{    
    "nome" : "Estabelecimento VI",
    "email" : "[email protected]",
    "localizacao" : {
        "endereco" : "R. Canuto do Val, 41 - Santa Cecilia, São Paulo - SP, 01224-040",
        "coordinates" : [ 
            -23.54062, 
            -46.65114
        ],
        "type" : "Point"
    }
}

アゴラVamos fazerは、Usendo O Insomnia Omクライアントパラシュートで降下しているマニュアルを通します.

ファテンドゥOレクイエムhttp://localhost:8080/api/estabelecimento?endereco=R . BGトビアス, 247
reposta :
[
  {
    "nome": "Estabelecimento II",
    "email": "[email protected]",
    "endereco": "R. Brg. Tobias, 206 - Santa Ifigênia, São Paulo - SP, 01032-000"
  },
  {
    "nome": "Estabelecimento III",
    "email": "[email protected]",
    "endereco": "Av. Cásper Líbero, 42 - Centro Histórico De São Paulo, São Paulo - SP, 01033-000"
  },
  {
    "nome": "Mercado I",
    "email": "[email protected]",
    "endereco": "Rua Brigadeiro Tobias n 780"
  },
  {
    "nome": "Estabelecimento IV",
    "email": "[email protected]",
    "endereco": "Av. Rio Branco, 630 - República, São Paulo - SP, 01205-000"
  }
]

Projeto Final


o projeto最終的なvocは、pode enconaraqui