Novos cartões CUBA

mapas


Trabalhar com dados geoespaciais e mapas de mapeamento é parte integrante de muitos aplicativos de negócios. Pode ser sistemas de informações regionais e da cidade, aplicativos para a indústria de petróleo e gás, sistemas de gerenciamento de infraestrutura de transporte, bem como serviços de entrega e muitos outros. Em nossa plataforma CUBA, para criar esses aplicativos, além dos recursos básicos fornecidos imediatamente, existe um conjunto bastante extenso de complementos e componentes . Um deles é o Cartas e Mapas , que, além de exibir gráficos, permite integrar o Google Maps na parte visual do aplicativo. No ano passado, o Google atualizou os termos de uso de seus serviços de mapas, o que levou a um aumento no custo, e também introduziu a condição para a presença obrigatória de um perfil de pagamento para o uso da API. Essas circunstâncias levaram a maioria de nossos clientes a pensar em fornecedores alternativos de cartões, e fomos solicitados a desenvolver um novo componente de cartão.


Agora temos o prazer de apresentar um componente completamente novo - o CUBA Maps . O CUBA Maps complementa a funcionalidade do aplicativo com uma representação visual e ferramentas intuitivas para editar dados geoespaciais. O componente trabalha com dados rasterizados e vetoriais. Você pode usar qualquer provedor de mapas compatível com o protocolo Web Map Service ou fornecer blocos no formato XYZ como dados rasterizados. Para trabalhar com dados vetoriais, o componente usa tipos de dados geométricos (ponto, polilinha, polígono) da biblioteca JTS Topology Suite (JTS) - a biblioteca Java mais popular para trabalhar com dados geoespaciais. O componente fornece todas as ferramentas necessárias para criar um sistema abrangente de informações geográficas baseado no CUBA.


Neste artigo, falaremos sobre os novos recursos oferecidos pelo componente Mapas e também o compararemos com o nosso componente de mapa anterior.


Estrutura baseada em camadas


O componente suporta a estrutura multicamada tradicional que é amplamente usada em sistemas de informações geográficas profissionais. As camadas são divididas principalmente em raster e vetor. As camadas rasterizadas consistem em imagens rasterizadas, enquanto as camadas vetoriais contêm geometrias vetoriais.


O componente suporta os seguintes tipos de camadas:


  • A camada de bloco exibe os blocos fornecidos pelos serviços de bloco no formato XYZ.
  • A camada Serviço de Mapa da Web (WMS) exibe as imagens fornecidas pelos serviços WMS.
  • A camada vetorial contém objetos geográficos (entidades com atributos geométricos).

Essas camadas são elementos estruturais dos mapas. Por exemplo, a camada inferior pode ser um mapa base constituído por ladrilhos, a segunda camada pode conter polígonos que descrevem unidades territoriais, por exemplo, regiões e a camada superior pode conter pontos geográficos (localização de clientes, lojas, etc.). Sobrepondo essas camadas umas sobre as outras, obtemos o mapa final:


camadas


Graças a essa abordagem, você pode criar mapas claramente estruturados com qualquer conteúdo.


O CUBA Maps fornece um novo componente visual - GeoMap . No descritor XML do componente, você pode definir os parâmetros básicos do mapa, bem como um conjunto de camadas exibidas. Um exemplo dessa configuração:


 <maps:geoMap id="map" height="600px" width="100%" center="37.615, 55.752" zoom="10"> <maps:layers selectedLayer="addressLayer"> <maps:tile id="tiles" tileProvider="maps_OpenStreetMap"/> <maps:vector id="territoryLayer" dataContainer="territoryDc"/> <maps:vector id="addressLayer" dataContainer="addressDc" editable="true"/> </maps:layers> </maps:geoMap> 

Essa abordagem permite obter mais flexibilidade que faltava em gráficos e mapas :


  • Camadas. Essa estrutura permite criar cartões com qualquer conteúdo, por exemplo, combinar blocos fornecidos por vários serviços.
  • As camadas fornecem uma abstração que reúne objetos homogêneos. No componente Gráficos e mapas , todo o conteúdo do mapa (por exemplo, pontos, polígonos etc.) foi despejado em uma pilha comum no componente da interface do usuário. Para estruturar de alguma forma esses objetos, as equipes do projeto tiveram que escrever uma lógica adicional.
  • Um método declarativo para descrever camadas. Como mostrado no exemplo acima, você pode especificar completamente a estrutura do mapa (conjunto de camadas) no descritor XML. Em muitos casos, isso é suficiente para não implementar nenhuma lógica adicional no controlador de tela. Usando gráficos e mapas , era quase impossível fazer sem escrever lógica adicional.

O uso de camadas de bloco ou WMS permite trabalhar com qualquer provedor de mapas preferido. Você não está vinculado a um provedor específico, como era em gráficos e mapas .


As camadas vetoriais simplificam bastante a exibição, a edição interativa e o desenho de objetos geográficos no mapa.


Também é importante notar que o componente visual do GeoMap por padrão, possui uma camada auxiliar especial - Canvas . O Canvas fornece uma API conveniente para exibir e desenhar geometrias (pontos, polilinhas, polígonos) em um mapa. Abordaremos exemplos de uso do Canvas mais adiante neste artigo.


Objetos geográficos


Suponha que tenhamos uma entidade que contenha um atributo associado à geometria (ponto, polilinha, polígono). Vamos chamar essa entidade de geo-objeto . Portanto, o componente simplifica bastante o trabalho com objetos geográficos.


Por exemplo, considere o objeto geográfico Endereço :


 @Entity public class Address extends StandardEntity { ... @Column(name = "LOCATION") @Geometry @MetaProperty(datatype = "GeoPoint") @Convert(converter = CubaPointWKTConverter.class) protected Point location; ... } 

Ele possui um atributo de location do tipo org.locationtech.jts.geom.Point da org.locationtech.jts.geom.Point JTS Topology Suite (JTS). O componente suporta os seguintes tipos geométricos do JTS:


  • org.locationtech.jts.geom.Point - período.
  • org.locationtech.jts.geom.LineString - polilinha.
  • org.locationtech.jts.geom.Polygon - polygon.

O atributo location é marcado com a anotação @Geometry . Esta anotação anuncia que o valor desse atributo deve ser usado ao exibir um objeto geográfico no mapa. O atributo também é marcado com as seguintes anotações:


  • @MetaProperty - nesse caso, usado para indicar o atributo do tipo de dados . A interface Datatype usada pela estrutura CUBA para converter valores para e de uma sequência.
  • @Convert - Define um conversor JPA para um atributo persistente. O conversor JPA converte valores de atributo entre suas representações no banco de dados e no código Java. O componente fornece um conjunto de tipos de dados espaciais e conversores JPA. Mais informações estão disponíveis na documentação do componente. Você também pode usar sua própria implementação do conversor JPA, o que possibilita trabalhar com várias fontes de dados espaciais (por exemplo, PostGIS ).

Portanto, para transformar uma entidade em um geo-objeto, você precisa definir um atributo do tipo de geometria JTS e anotá-lo com @Geometry . Há outra opção - crie um atributo não persistente, fornecendo métodos getter / setter. Isso pode ser útil se você não desejar fazer alterações no modelo de dados e gerar novamente scripts DDL.


Por exemplo, considere a entidade Endereço com atributos separados para latitude e longitude:


 import com.haulmont.addon.maps.gis.utils.GeometryUtils; ... @Entity public class Address extends StandardEntity { ... @Column(name = "LATITUDE") protected Double latitude; @Column(name = "LONGITUDE") protected Double longitude; ... @Geometry @MetaProperty(datatype = "GeoPoint", related = {"latitude", "longitude"}) public Point getLocation() { if (getLatitude() == null || getLongitude() == null) { return null; } return GeometryUtils.createPoint(getLongitude(), getLatitude()); } @Geometry @MetaProperty(datatype = "GeoPoint") public void setLocation(Point point) { Point prevValue = getLocation(); if (point == null) { setLatitude(null); setLongitude(null); } else { setLatitude(point.getY()); setLongitude(point.getX()); } propertyChanged("location", prevValue, point); } ... } 

Se você decidir usar essa abordagem, verifique se o método propertyChanged é chamado no setter, pois o componente responde a esse evento atualizando a geometria no mapa.


Agora que preparamos a classe do nosso objeto geográfico, podemos adicionar instâncias dessa classe à camada vetorial. Uma camada vetorial é essencialmente um elemento de conexão entre dados (objetos geográficos) e um mapa. Para conectar objetos geográficos a uma camada, é necessário transferir o contêiner de dados ou, no caso de telas obsoletas (até a versão 7 do CUBA), a fonte de dados para a camada vetorial. Isso pode ser feito em um descritor XML:


 <maps:geoMap id="map"> <maps:layers> ... <maps:vector id="addressesLayer" dataContainer="addressesDc"/> </maps:layers> </maps:geoMap> 

Como resultado, as instâncias da classe Address contidas no contêiner AddressDc serão exibidas no mapa.


Vamos considerar uma tarefa elementar: criar uma tela para editar um objeto geográfico com um mapa, onde você pode editar a geometria do objeto. Para resolver o problema, você precisa declarar o componente visual do GeoMap no descritor XML da tela de edição e adicionar uma camada vetorial associada ao contêiner que contém o objeto geográfico editado:


 <maps:geoMap id="map" height="600px" width="100%" center="37.615, 55.752" zoom="10"> <maps:layers selectedLayer="addressLayer"> <maps:tile ..."/> <maps:vector id="addressLayer" dataContainer="addressDc" editable="true"/> </maps:layers> </maps:geoMap> 

Se você marcar a camada vetorial como editável, a edição interativa do objeto geográfico no mapa será ativada. Se a geometria do objeto estiver vazia, o mapa mudará automaticamente para o modo de desenho. Como você pode ver, para resolver o problema, basta declarar uma camada vetorial no mapa e passá-la para o contêiner de dados / fonte de dados.


Isso é tudo. Se usamos Gráficos e Mapas para resolver o mesmo problema, teríamos que escrever bastante código no controlador de tela para fornecer funcionalidade semelhante. Com o novo componente do Maps, resolver esses problemas é muito mais simples.


Tela


Há momentos em que você precisa trabalhar e não com entidades. Em vez disso, você deseja que uma API simples adicione e desenhe geometrias em um mapa, como era em gráficos e mapas . Para isso, o componente visual do GeoMap possui uma camada especial - Canvas . Essa é uma camada auxiliar, que está no mapa por padrão e fornece uma API simples para adicionar e desenhar geometrias no mapa. Você pode map.getCanvas() mapa de map.getCanvas() chamando o método map.getCanvas() .


A seguir, veremos algumas tarefas simples, como elas foram resolvidas em gráficos e mapas e como fazer o mesmo usando o Canvas.


Exibir geometrias em um mapa


Em Cartas e Mapas, os objetos de geometria foram criados usando o componente visual do mapa, usado como fábrica e, em seguida, adicionados ao mapa:


 Marker marker = map.createMarker(); GeoPoint position = map.createGeoPoint(lat, lon); marker.setPosition(position); map.addMarker(marker); 

O novo componente do Google Maps trabalha diretamente com as classes da biblioteca JTS:


 CanvasLayer canvasLayer = map.getCanvas(); Point point = address.getLocation(); canvasLayer.addPoint(point); 

Edição de geometria


Em gráficos e mapas, você pode designar a geometria como editável. Quando essas geometrias foram alteradas através da interface do usuário, os eventos correspondentes foram chamados:


 Marker marker = map.createMarker(); GeoPoint position = map.createGeoPoint(lat, lon); marker.setPosition(position); marker.setDraggable(true); map.addMarker(marker); map.addMarkerDragListener(event -> { // do something }); 

No componente Mapas , ao adicionar a geometria JTS ao Canvas, o método correspondente retorna um objeto especial que é uma representação dessa geometria no mapa: CanvasLayer.Point , CanvasLayer.Polyline ou CanvasLayer.Polygon . Este objeto possui uma interface fluente para definir vários parâmetros de geometria, também pode ser usado para assinar eventos relacionados à geometria ou para excluir a geometria de um Canvas.


 CanvasLayer canvasLayer = map.getCanvas(); CanvasLayer.Point location = canvasLayer.addPoint(address.getLocation()); location.setEditable(true) .setPopupContent(address.getName()) .addModifiedListener(modifiedEvent -> address.setLocation(modifiedEvent.getGeometry())); 

Desenho de geometria


Nos antigos gráficos e mapas complementares, havia um componente auxiliar - DrawingOptions . Foi usado para ativar a capacidade de desenhar no mapa. Depois que a geometria foi desenhada, o evento correspondente foi gerado:


 DrawingOptions options = new DrawingOptions(); PolygonOptions polygonOptions = new PolygonOptions(true, true, "#993366", 0.6); ControlOptions controlOptions = new ControlOptions( Position.TOP_CENTER, Arrays.asList(OverlayType.POLYGON)); options.setEnableDrawingControl(true); options.setPolygonOptions(polygonOptions); options.setDrawingControlOptions(controlOptions); options.setInitialDrawingMode(OverlayType.POLYGON); map.setDrawingOptions(options); map.addPolygonCompleteListener(event -> { //do something }); 

O componente Mapas torna o mesmo muito mais fácil. O novo Maps Canvas contém um conjunto de métodos para desenhar geometrias. Por exemplo, para desenhar um polígono, use o método canvas.drawPolygon() . Depois de chamar esse método, o mapa mudará para o modo de desenho de polígono. O método aceita a função Consumer<CanvasLayer.Polygon> , na qual você pode executar ações adicionais com o polígono desenhado.


 canvasLayer.drawPolygon(polygon -> { territory.setPolygon(polygon.getGeometry()); }); 

Ferramentas de análise geográfica


Agrupamento


Outra ferramenta útil presente no novo componente Maps é o agrupamento de pontos. Se a camada consistir em um grande número de pontos, você poderá ativar o agrupamento para agrupar pontos próximos em clusters, para que o mapa pareça mais limpo e com melhor percepção:


agrupar


O cluster é ativado adicionando a marca de cluster dentro da marca de vector no descritor XML:


 <maps:vector id="locations" dataContainer="locationsDc" > <maps:cluster/> </maps:vector> 

Você também pode habilitar o cluster com base em pesos de pontos. O peso do ponto é o valor do atributo especificado no parâmetro weightProperty .


 <maps:vector id="orders" dataContainer="ordersDc" > <maps:cluster weightProperty="amount"/> </maps:vector> 

Heatmaps


Os mapas de calor são uma representação visual da densidade de dados em vários locais geográficos. O componente visual do GeoMap contém um método para adicionar um mapa de calor: addHeatMap(Map<Point, Double> intensityMap) .


mapa de calor


Conclusão


O processamento, análise e visualização de dados geoespaciais é um elemento essencial de muitos aplicativos de negócios. O componente CUBA Maps fornecerá ao seu aplicativo CUBA todas as ferramentas necessárias para implementar essa funcionalidade.


Uma estrutura baseada em camadas ajuda na construção de mapas com qualquer conteúdo. Com camadas lado a lado / WMS, você pode usar qualquer provedor necessário como placa base. As camadas vetoriais permitem trabalhar efetivamente com conjuntos de objetos geográficos homogêneos. O Canvas fornece uma API simples para exibir e desenhar geometrias em um mapa.


O componente trabalha com tipos espaciais da biblioteca JTS, o que o torna compatível com muitas outras estruturas (por exemplo, GeoTools ) para resolver uma ampla variedade de tarefas relacionadas ao processamento e análise de dados geográficos.


Esperamos que você goste do componente. Aguardando seu feedback!

Source: https://habr.com/ru/post/pt464517/


All Articles