├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── gs-web-elasticsearch ├── LICENSE ├── doc │ ├── images │ │ ├── elasticsearch_configuration.png │ │ ├── elasticsearch_fieldlist.png │ │ ├── elasticsearch_fieldlist_edit.png │ │ ├── elasticsearch_logging.png │ │ └── elasticsearch_store.png │ └── index.rst ├── pom.xml └── src │ ├── assembly │ └── dist.xml │ └── main │ ├── java │ ├── applicationContext.xml │ └── mil │ │ └── nga │ │ └── giat │ │ └── elasticsearch │ │ ├── ElasticAttributeProvider.java │ │ ├── ElasticConfigurationPage.html │ │ ├── ElasticConfigurationPage.java │ │ ├── ElasticConfigurationPanel.html │ │ ├── ElasticConfigurationPanel.java │ │ ├── ElasticConfigurationPanelInfo.java │ │ ├── ElasticFeatureTypeCallback.java │ │ ├── ElasticXStreamInitializer.java │ │ └── ElasticXStreamPersisterInitializer.java │ └── resources │ └── GeoServerApplication.properties ├── gt-elasticsearch-process ├── LGPL ├── LICENSE ├── pom.xml └── src │ ├── main │ ├── java │ │ └── mil │ │ │ └── nga │ │ │ └── giat │ │ │ └── process │ │ │ └── elasticsearch │ │ │ ├── BBOXRemovingFilterVisitor.java │ │ │ ├── BasicGeoHashGrid.java │ │ │ ├── GeoHashGrid.java │ │ │ ├── GeoHashGridProcess.java │ │ │ ├── GridCell.java │ │ │ ├── GridCoverageUtil.java │ │ │ ├── MetricGeoHashGrid.java │ │ │ ├── NestedAggGeoHashGrid.java │ │ │ └── RasterScale.java │ └── resources │ │ └── META-INF │ │ └── services │ │ ├── org.geotools.process.ProcessFactory │ │ └── org.geotools.process.vector.VectorProcess │ └── test │ └── java │ └── mil │ └── nga │ └── giat │ └── process │ └── elasticsearch │ ├── GeoHashGridProcessTest.java │ ├── GeoHashGridTest.java │ ├── GridCoverageUtilTest.java │ ├── MetricGeoHashGridTest.java │ ├── NestedAggGeoHashGridTest.java │ ├── RasterScaleTest.java │ └── TestUtil.java ├── gt-elasticsearch ├── LGPL ├── LICENSE ├── pom.xml └── src │ ├── main │ ├── java │ │ └── mil │ │ │ └── nga │ │ │ └── giat │ │ │ └── data │ │ │ └── elasticsearch │ │ │ ├── ElasticAggregation.java │ │ │ ├── ElasticAttribute.java │ │ │ ├── ElasticCapabilities.java │ │ │ ├── ElasticClient.java │ │ │ ├── ElasticConstants.java │ │ │ ├── ElasticDataStore.java │ │ │ ├── ElasticDataStoreFactory.java │ │ │ ├── ElasticFeatureReader.java │ │ │ ├── ElasticFeatureReaderScroll.java │ │ │ ├── ElasticFeatureSource.java │ │ │ ├── ElasticFeatureTypeBuilder.java │ │ │ ├── ElasticHit.java │ │ │ ├── ElasticLayerConfiguration.java │ │ │ ├── ElasticMappings.java │ │ │ ├── ElasticParserUtil.java │ │ │ ├── ElasticRequest.java │ │ │ ├── ElasticResponse.java │ │ │ ├── ElasticResults.java │ │ │ ├── FilterToElastic.java │ │ │ ├── FilterToElasticException.java │ │ │ ├── FilterToElasticHelper.java │ │ │ ├── GeohashUtil.java │ │ │ ├── RestElasticClient.java │ │ │ └── TotalDeserializer.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── org.geotools.data.DataStoreFactorySpi │ └── test │ ├── java │ └── mil │ │ └── nga │ │ └── giat │ │ └── data │ │ └── elasticsearch │ │ ├── ElasticAggregationReaderTest.java │ │ ├── ElasticAttributeTest.java │ │ ├── ElasticDataStoreFinderIT.java │ │ ├── ElasticDataStoreIT.java │ │ ├── ElasticDatastoreFactoryTest.java │ │ ├── ElasticFeatureFilterIT.java │ │ ├── ElasticFilterTest.java │ │ ├── ElasticGeometryFilterIT.java │ │ ├── ElasticParserUtilTest.java │ │ ├── ElasticResponseTest.java │ │ ├── ElasticTemporalFilterIT.java │ │ ├── ElasticTestSupport.java │ │ ├── ElasticViewParametersFilterIT.java │ │ ├── GeohashUtilTest.java │ │ ├── RandomGeometryBuilder.java │ │ └── RestElasticClientTest.java │ └── resources │ ├── README.md │ ├── active_mappings.json │ ├── active_mappings_legacy.json │ ├── active_mappings_ng.json │ ├── log4j.properties │ ├── logging.properties │ ├── mockito-extensions │ └── org.mockito.plugins.MockMaker │ ├── requirements.txt │ ├── test_index.py │ └── wifiAccessPoint.json ├── joda-shaded ├── LICENSE.txt ├── NOTICE.txt ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── elasticsearch │ └── common │ └── Strings.java └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | **target/ 2 | **.settings/ 3 | **.project 4 | **.classpath 5 | *.prefs 6 | *.log 7 | *.iml 8 | **idea/ 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: true 2 | services: 3 | - docker 4 | language: java 5 | jdk: 6 | - openjdk8 7 | env: 8 | matrix: 9 | - GEOTOOLS_VERSION='22.0' GEOSERVER_VERSION='2.16.0' ES_VERSION='7.4.0' 10 | - GEOTOOLS_VERSION='22.0' GEOSERVER_VERSION='2.16.0' ES_VERSION='6.8.3' 11 | - GEOTOOLS_VERSION='22.0' GEOSERVER_VERSION='2.16.0' ES_VERSION='5.6.16' 12 | - GEOTOOLS_VERSION='22.0' GEOSERVER_VERSION='2.16.0' ES_VERSION='2.4.5' ARGS='-Ddocker.image=elasticsearch' 13 | - GEOTOOLS_VERSION='21.3' GEOSERVER_VERSION='2.15.3' ES_VERSION='7.4.0' 14 | - GEOTOOLS_VERSION='21.3' GEOSERVER_VERSION='2.15.3' ES_VERSION='6.8.3' 15 | - GEOTOOLS_VERSION='21.3' GEOSERVER_VERSION='2.15.3' ES_VERSION='5.6.16' 16 | - GEOTOOLS_VERSION='21.3' GEOSERVER_VERSION='2.15.3' ES_VERSION='2.4.5' ARGS='-Ddocker.image=elasticsearch' 17 | cache: 18 | directories: 19 | - "$HOME/.m2" 20 | install: 21 | - mvn --version 22 | - travis_retry mvn install -DskipTests=true -Dskip.integration.tests=true -B -Dgeotools.version=${GEOTOOLS_VERSION} -Dgeoserver.version=${GEOSERVER_VERSION} -Des.test.version=${ES_VERSION} ${ARGS} 23 | script: 24 | - sudo sysctl -w vm.max_map_count=262144 25 | - travis_retry mvn verify -Dgeotools.version=${GEOTOOLS_VERSION} -Dgeoserver.version=${GEOSERVER_VERSION} -Des.test.version=${ES_VERSION} ${ARGS} 26 | after_success: 27 | - mvn coveralls:report 28 | before_deploy: 29 | - mvn assembly:assembly -pl gs-web-elasticsearch 30 | - export PLUGIN_FILE="$(ls gs-web-elasticsearch/target/elasticgeo*.zip)"; 31 | - echo "Deploying $PLUGIN_FILE to GitHub releases" 32 | deploy: 33 | provider: releases 34 | api_key: 35 | secure: UiMZusRpTSbs3BQWZxououT1VPj8LzUN4UYH8H+PcPdEEZfx/lDNCxlaj1YG8eVCflJL1TItPdU8jIEU435weP4v3cnSyUjq/3Oc4ey9CK/iuphoqvkqfCgRxpVsj9wuZkJqLobi9+9JFLz6NziCnX9ME5WxRgMPlfRU2jot/GM= 36 | file_glob: true 37 | skip_cleanup: true 38 | file: "$PLUGIN_FILE" 39 | on: 40 | tags: true 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ElasticGeo 2 | 3 | 4 | Travis-CI test status 6 | 7 |
8 | 9 | Coverage Status 11 | 12 | 13 | ElasticGeo provides a GeoTools data store that allows geospatial features from an Elasticsearch index to be published via OGC services using GeoServer. 14 | 15 | ### Pull Request 16 | 17 | If you'd like to contribute to this project, please make a pull request. We'll review the pull request and discuss the changes. All pull request contributions to this project will be released under the appropriate license conditions discussed below. 18 | 19 | Software source code previously released under an open source license and then modified by NGA staff is considered a "joint work" (see 17 USC 101); it is partially copyrighted, partially public domain, and as a whole is protected by the copyrights of the non-government authors and must be released according to the terms of the original open source license. 20 | 21 | ### Project relies upon: 22 | 23 | GeoTools under [LGPL v 2.1](http://geotools.org/about.html) 24 | 25 | GeoServer under [GPL v 2 with later option](http://geoserver.org/license/) 26 | 27 | ElasticGeo under [LGPL v 2.1](https://github.com/matsjg/elasticgeo) 28 | 29 | Elasticsearch under [Apache License v 2.0](https://github.com/elastic/elasticsearch/blob/master/LICENSE.txt) 30 | 31 | ### Documentation 32 | 33 | [Read more](gs-web-elasticsearch/doc/index.rst) 34 | 35 | 36 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/doc/images/elasticsearch_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngageoint/elasticgeo/91c2a99ccea6988cc5d0f96a108c690b7b9c6ea2/gs-web-elasticsearch/doc/images/elasticsearch_configuration.png -------------------------------------------------------------------------------- /gs-web-elasticsearch/doc/images/elasticsearch_fieldlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngageoint/elasticgeo/91c2a99ccea6988cc5d0f96a108c690b7b9c6ea2/gs-web-elasticsearch/doc/images/elasticsearch_fieldlist.png -------------------------------------------------------------------------------- /gs-web-elasticsearch/doc/images/elasticsearch_fieldlist_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngageoint/elasticgeo/91c2a99ccea6988cc5d0f96a108c690b7b9c6ea2/gs-web-elasticsearch/doc/images/elasticsearch_fieldlist_edit.png -------------------------------------------------------------------------------- /gs-web-elasticsearch/doc/images/elasticsearch_logging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngageoint/elasticgeo/91c2a99ccea6988cc5d0f96a108c690b7b9c6ea2/gs-web-elasticsearch/doc/images/elasticsearch_logging.png -------------------------------------------------------------------------------- /gs-web-elasticsearch/doc/images/elasticsearch_store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngageoint/elasticgeo/91c2a99ccea6988cc5d0f96a108c690b7b9c6ea2/gs-web-elasticsearch/doc/images/elasticsearch_store.png -------------------------------------------------------------------------------- /gs-web-elasticsearch/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | 8 | elasticgeo 9 | mil.nga.giat 10 | 2.16-SNAPSHOT 11 | 12 | gs-web-elasticsearch 13 | 2.16-SNAPSHOT 14 | jar 15 | GeoServer Elasticsearch Module 16 | 17 | 18 | mil.nga.giat 19 | gt-elasticsearch 20 | ${project.version} 21 | 22 | 23 | mil.nga.giat 24 | gt-elasticsearch-process 25 | ${project.version} 26 | 27 | 28 | 29 | org.geoserver 30 | gs-main 31 | ${geoserver.version} 32 | provided 33 | 34 | 35 | org.geoserver.web 36 | gs-web-core 37 | ${geoserver.version} 38 | provided 39 | 40 | 41 | org.geotools 42 | gt-main 43 | ${geotools.version} 44 | provided 45 | 46 | 47 | org.geoserver.web 48 | gs-web-core 49 | ${geoserver.version} 50 | test-jar 51 | test 52 | 53 | 54 | org.geoserver 55 | gs-main 56 | ${geoserver.version} 57 | test-jar 58 | test 59 | 60 | 61 | com.mockrunner 62 | mockrunner 63 | 0.3.6 64 | test 65 | 66 | 67 | junit 68 | junit 69 | 4.11 70 | test 71 | 72 | 73 | 74 | 75 | 76 | ${basedir}/src/main/java 77 | 78 | applicationContext.xml 79 | **/*.html 80 | 81 | 82 | 83 | ${basedir}/src/main/resources 84 | 85 | **/* 86 | 87 | 88 | 89 | 90 | 91 | maven-assembly-plugin 92 | 2.4 93 | 94 | src/assembly/dist.xml 95 | false 96 | elasticgeo-${project.version} 97 | 98 | 99 | 100 | org.apache.maven.plugins 101 | maven-shade-plugin 102 | 2.2 103 | 104 | 105 | package 106 | 107 | shade 108 | 109 | 110 | 111 | 112 | *:pom:* 113 | *:maven*:* 114 | commons-codec 115 | commons-collections 116 | commons-httpclient 117 | commons-logging 118 | *:guava:* 119 | javax.media:jai*:* 120 | *:jts:* 121 | *:*:jsr305 122 | log4j:log4j 123 | 124 | 125 | 126 | 127 | *:* 128 | 129 | META-INF/maven/** 130 | META-INF/*.SF 131 | META-INF/*.DSA 132 | META-INF/*.RSA 133 | META-INF/DEPENDENCIES 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | META-INF/LICENSE 142 | LICENSE 143 | 144 | 145 | false 146 | 147 | 148 | false 149 | false 150 | elasticgeo-${project.version} 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/src/assembly/dist.xml: -------------------------------------------------------------------------------- 1 | 4 | bin 5 | 6 | zip 7 | 8 | false 9 | 10 | 11 | ${project.build.directory} 12 | / 13 | 14 | elasticgeo*.jar 15 | 16 | 17 | 18 | . 19 | / 20 | 21 | LICENSE 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/src/main/java/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Elasticsearch 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/src/main/java/mil/nga/giat/elasticsearch/ElasticAttributeProvider.java: -------------------------------------------------------------------------------- 1 | /* (c) 2014 Open Source Geospatial Foundation - all rights reserved 2 | * This code is licensed under the GPL 2.0 license, available at the root 3 | * application directory. 4 | */ 5 | 6 | package mil.nga.giat.elasticsearch; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | import mil.nga.giat.data.elasticsearch.ElasticAttribute; 12 | 13 | import org.geoserver.web.wicket.GeoServerDataProvider; 14 | 15 | /** 16 | * 17 | * Provide attributes from Elasticsearch fields. 18 | * 19 | */ 20 | class ElasticAttributeProvider extends GeoServerDataProvider { 21 | 22 | private static final long serialVersionUID = -1021780286733349153L; 23 | 24 | private final List attributes; 25 | 26 | /** 27 | * Name of field 28 | */ 29 | static final Property NAME = new BeanProperty<>("name", 30 | "displayName"); 31 | 32 | /** 33 | * Class type of field 34 | */ 35 | static final Property TYPE = new AbstractProperty( 36 | "type") { 37 | 38 | private static final long serialVersionUID = 4454312983828267130L; 39 | 40 | @Override 41 | public Object getPropertyValue(ElasticAttribute item) { 42 | if (item.getType() != null) { 43 | return item.getType().getSimpleName(); 44 | } 45 | return null; 46 | } 47 | 48 | }; 49 | 50 | /** 51 | * Mark as the default geometry 52 | */ 53 | static final Property DEFAULT_GEOMETRY = new BeanProperty<>( 54 | "defaultGeometry", "defaultGeometry"); 55 | 56 | /** 57 | * SRID of geometric field 58 | */ 59 | static final Property SRID = new BeanProperty<>("srid", "srid"); 60 | 61 | /** 62 | * Use field in datastore 63 | */ 64 | static final Property USE = new BeanProperty<>("use", "use"); 65 | 66 | /** 67 | * Store if the field is in use in datastore 68 | */ 69 | static final Property DATE_FORMAT = new BeanProperty<>("dateFormat", "dateFormat"); 70 | 71 | /** 72 | * If field is analyzed 73 | */ 74 | static final Property ANALYZED = new BeanProperty<>("analyzed", "analyzed"); 75 | 76 | /** 77 | * If field is stored 78 | */ 79 | static final Property STORED = new BeanProperty<>("stored", "stored"); 80 | 81 | /** 82 | * Order of the field 83 | */ 84 | static final Property ORDER = new BeanProperty<>("order", "order"); 85 | 86 | /** 87 | * Custom name of the field 88 | */ 89 | static final Property CUSTOM_NAME = new BeanProperty<>("customName", "customName"); 90 | 91 | /** 92 | * Build attribute provider 93 | * 94 | * @param attributes list to use as source for provider 95 | */ 96 | public ElasticAttributeProvider(List attributes) { 97 | this.attributes = attributes; 98 | } 99 | 100 | @Override 101 | protected List> getProperties() { 102 | return Arrays.asList(USE, NAME, TYPE, ORDER, CUSTOM_NAME, DEFAULT_GEOMETRY, STORED, ANALYZED, SRID, DATE_FORMAT); 103 | } 104 | 105 | @Override 106 | protected List getItems() { 107 | return attributes; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/src/main/java/mil/nga/giat/elasticsearch/ElasticConfigurationPage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <wicket:message key="title">Elasticsearch fields configuration</wicket:message> 5 | 6 | 7 | 8 |
9 | 10 | Use All 11 | 12 | Short Names 13 |
14 |
15 |
[Feedback Panel]
16 | 21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/src/main/java/mil/nga/giat/elasticsearch/ElasticConfigurationPanel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/src/main/java/mil/nga/giat/elasticsearch/ElasticConfigurationPanel.java: -------------------------------------------------------------------------------- 1 | /* (c) 2014 Open Source Geospatial Foundation - all rights reserved 2 | * This code is licensed under the GPL 2.0 license, available at the root 3 | * application directory. 4 | */ 5 | 6 | package mil.nga.giat.elasticsearch; 7 | 8 | import java.io.IOException; 9 | import java.util.List; 10 | import java.util.logging.Level; 11 | 12 | import mil.nga.giat.data.elasticsearch.ElasticAttribute; 13 | import mil.nga.giat.data.elasticsearch.ElasticDataStore; 14 | import mil.nga.giat.data.elasticsearch.ElasticLayerConfiguration; 15 | 16 | import org.apache.wicket.Component; 17 | import org.apache.wicket.MarkupContainer; 18 | import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior; 19 | import org.apache.wicket.ajax.AjaxRequestTarget; 20 | import org.apache.wicket.ajax.markup.html.AjaxLink; 21 | import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; 22 | import org.apache.wicket.markup.head.IHeaderResponse; 23 | import org.apache.wicket.markup.head.OnLoadHeaderItem; 24 | import org.apache.wicket.markup.html.panel.Fragment; 25 | import org.apache.wicket.model.IModel; 26 | import org.geoserver.catalog.Catalog; 27 | import org.geoserver.catalog.CatalogBuilder; 28 | import org.geoserver.catalog.DataStoreInfo; 29 | import org.geoserver.catalog.FeatureTypeInfo; 30 | import org.geoserver.catalog.LayerInfo; 31 | import org.geoserver.catalog.ResourceInfo; 32 | import org.geoserver.web.GeoServerApplication; 33 | import org.geoserver.web.data.resource.ResourceConfigurationPage; 34 | import org.geoserver.web.data.resource.ResourceConfigurationPanel; 35 | import org.geoserver.web.wicket.ParamResourceModel; 36 | import org.geotools.feature.NameImpl; 37 | import org.opengis.feature.type.Name; 38 | 39 | /** 40 | * Resource configuration panel to show a link to open Elasticsearch attribute 41 | * modal dialog
If the Elasticsearch attribute are not configured for 42 | * current layer, the modal dialog will be open at first resource configuration 43 | * window opening
After modal dialog is closed the resource page is 44 | * reloaded and feature configuration table updated 45 | * 46 | */ 47 | @SuppressWarnings("WeakerAccess") 48 | public class ElasticConfigurationPanel extends ResourceConfigurationPanel { 49 | 50 | private static final long serialVersionUID = 3382530429105288433L; 51 | 52 | private LayerInfo _layerInfo; 53 | 54 | private ElasticLayerConfiguration _layerConfig; 55 | 56 | /** 57 | * Adds Elasticsearch configuration panel link, configure modal dialog and 58 | * implements modal callback. 59 | * 60 | * @see ElasticConfigurationPage#done 61 | */ 62 | 63 | public ElasticConfigurationPanel(final String panelId, final IModel model) { 64 | super(panelId, model); 65 | final FeatureTypeInfo fti = (FeatureTypeInfo) model.getObject(); 66 | 67 | final ModalWindow modal = new ModalWindow("modal"); 68 | modal.setInitialWidth(800); 69 | modal.setTitle(new ParamResourceModel("modalTitle", ElasticConfigurationPanel.this)); 70 | 71 | if (fti.getMetadata().get(ElasticLayerConfiguration.KEY) == null) { 72 | modal.add(new OpenWindowOnLoadBehavior()); 73 | } 74 | 75 | modal.setContent(new ElasticConfigurationPage(panelId, model) { 76 | @Override 77 | void done(AjaxRequestTarget target, LayerInfo layerInfo, 78 | ElasticLayerConfiguration layerConfig) { 79 | _layerInfo = layerInfo; 80 | _layerConfig = layerConfig; 81 | 82 | try { 83 | saveLayer((FeatureTypeInfo) getResourceInfo()); 84 | } catch (IOException e) { 85 | LOGGER.log(Level.SEVERE, e.getMessage(), e); 86 | error(new ParamResourceModel("creationFailure", this, e).getString()); 87 | } 88 | 89 | MarkupContainer parent = ElasticConfigurationPanel.this.getParent(); 90 | while (!(parent == null || parent instanceof ResourceConfigurationPage)) { 91 | parent = parent.getParent(); 92 | } 93 | 94 | if (parent != null) { 95 | ResourceInfo ri = ElasticConfigurationPanel.this.getResourceInfo(); 96 | ((ResourceConfigurationPage) parent).updateResource(ri, target); 97 | } 98 | 99 | modal.close(target); 100 | } 101 | }); 102 | add(modal); 103 | 104 | AjaxLink findLink = new AjaxLink("edit") { 105 | @Override 106 | public void onClick(AjaxRequestTarget target) { 107 | modal.show(target); 108 | } 109 | }; 110 | final Fragment attributePanel = new Fragment("esPanel", "esPanelFragment", this); 111 | attributePanel.setOutputMarkupId(true); 112 | add(attributePanel); 113 | attributePanel.add(findLink); 114 | } 115 | 116 | /* 117 | * Open modal dialog on window load 118 | */ 119 | private class OpenWindowOnLoadBehavior extends AbstractDefaultAjaxBehavior { 120 | @Override 121 | protected void respond(AjaxRequestTarget target) { 122 | ModalWindow window = (ModalWindow) getComponent(); 123 | window.show(target); 124 | } 125 | 126 | @Override 127 | public void renderHead(Component component, IHeaderResponse response) { 128 | response.render(OnLoadHeaderItem.forScript(getCallbackScript().toString())); 129 | } 130 | } 131 | 132 | private void saveLayer(FeatureTypeInfo ft) throws IOException { 133 | GeoServerApplication app = (GeoServerApplication) getApplication(); 134 | Catalog catalog = app.getCatalog(); 135 | 136 | String namespace = ft.getNamespace().getURI(); 137 | Name qualifiedName = new NameImpl(namespace, _layerInfo.getName()); 138 | LayerInfo layerInfo = catalog.getLayerByName(qualifiedName); 139 | 140 | boolean isNew = ft.getId() == null || app.getCatalog().getResource(ft.getId(),ResourceInfo.class) == null; 141 | 142 | FeatureTypeInfo typeInfo; 143 | if (layerInfo == null || isNew) { 144 | // New 145 | DataStoreInfo dsInfo; 146 | dsInfo = catalog.getStore(ft.getStore().getId(), DataStoreInfo.class); 147 | ElasticDataStore ds = (ElasticDataStore) dsInfo.getDataStore(null); 148 | CatalogBuilder builder = new CatalogBuilder(catalog); 149 | builder.setStore(dsInfo); 150 | ElasticLayerConfiguration layerConfig; 151 | layerConfig = new ElasticLayerConfiguration(_layerConfig); 152 | layerConfig.setLayerName(_layerInfo.getName()); 153 | layerConfig.getAttributes().clear(); 154 | List attributes = _layerConfig.getAttributes(); 155 | layerConfig.getAttributes().addAll(attributes); 156 | ds.setLayerConfiguration(layerConfig); 157 | 158 | FeatureTypeInfo _typeInfo = (FeatureTypeInfo) _layerInfo.getResource(); 159 | typeInfo = builder.buildFeatureType(ds.getFeatureSource(qualifiedName)); 160 | typeInfo.setName(_layerInfo.getName()); 161 | typeInfo.getMetadata().put(ElasticLayerConfiguration.KEY, layerConfig); 162 | typeInfo.setEnabled(_typeInfo.isEnabled()); 163 | typeInfo.setAdvertised(_typeInfo.isAdvertised()); 164 | typeInfo.setTitle(_typeInfo.getTitle()); 165 | typeInfo.setDescription(_typeInfo.getDescription()); 166 | typeInfo.setAbstract(_typeInfo.getAbstract()); 167 | typeInfo.getKeywords().addAll(_typeInfo.getKeywords()); 168 | typeInfo.getMetadataLinks().addAll(_typeInfo.getMetadataLinks()); 169 | typeInfo.getDataLinks().addAll(_typeInfo.getDataLinks()); 170 | typeInfo.setSRS(_typeInfo.getSRS()); 171 | typeInfo.setProjectionPolicy(_typeInfo.getProjectionPolicy()); 172 | typeInfo.setNativeBoundingBox(_typeInfo.getNativeBoundingBox()); 173 | typeInfo.setLatLonBoundingBox(_typeInfo.getLatLonBoundingBox()); 174 | typeInfo.setCircularArcPresent(_typeInfo.isCircularArcPresent()); 175 | typeInfo.setLinearizationTolerance(_typeInfo.getLinearizationTolerance()); 176 | 177 | layerInfo = builder.buildLayer(typeInfo); 178 | builder.updateLayer(layerInfo, _layerInfo); 179 | layerInfo.setName(_layerInfo.getName()); 180 | layerInfo.setResource(typeInfo); 181 | } else { 182 | // Update 183 | typeInfo = (FeatureTypeInfo) layerInfo.getResource(); 184 | typeInfo.getMetadata().put(ElasticLayerConfiguration.KEY, _layerConfig); 185 | } 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/src/main/java/mil/nga/giat/elasticsearch/ElasticConfigurationPanelInfo.java: -------------------------------------------------------------------------------- 1 | /* (c) 2014 Open Source Geospatial Foundation - all rights reserved 2 | * This code is licensed under the GPL 2.0 license, available at the root 3 | * application directory. 4 | */ 5 | 6 | package mil.nga.giat.elasticsearch; 7 | 8 | import org.geoserver.catalog.FeatureTypeInfo; 9 | import org.geoserver.platform.ExtensionPriority; 10 | import org.geoserver.web.data.resource.ResourceConfigurationPanelInfo; 11 | 12 | /** 13 | * 14 | * Implements ResourceConfigurationPanelInfo extension point to add Elasticsearch 15 | * attribute configuration link on resource page.
16 | * Priority is reduced under standard {@link ExtensionPriority#LOWEST} to shows 17 | * the Elasticsearch link after other panels. 18 | * 19 | */ 20 | class ElasticConfigurationPanelInfo extends ResourceConfigurationPanelInfo implements ExtensionPriority { 21 | 22 | private static final long serialVersionUID = 1485404586629946126L; 23 | 24 | @Override 25 | public boolean canHandle(Object obj) { 26 | boolean canHandle = false; 27 | if (obj instanceof FeatureTypeInfo) { 28 | FeatureTypeInfo fti = (FeatureTypeInfo) obj; 29 | for (String st : getSupportedTypes()) { 30 | if (fti.getStore().getType().equals(st)) { 31 | canHandle = true; 32 | break; 33 | } 34 | } 35 | } 36 | return canHandle; 37 | } 38 | 39 | @Override 40 | public int getPriority() { 41 | return ExtensionPriority.LOWEST + 1; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/src/main/java/mil/nga/giat/elasticsearch/ElasticFeatureTypeCallback.java: -------------------------------------------------------------------------------- 1 | /* (c) 2014 Open Source Geospatial Foundation - all rights reserved 2 | * This code is licensed under the GPL 2.0 license, available at the root 3 | * application directory. 4 | */ 5 | package mil.nga.giat.elasticsearch; 6 | 7 | import mil.nga.giat.data.elasticsearch.ElasticDataStore; 8 | import mil.nga.giat.data.elasticsearch.ElasticLayerConfiguration; 9 | import static mil.nga.giat.data.elasticsearch.ElasticLayerConfiguration.KEY; 10 | 11 | import org.geoserver.catalog.FeatureTypeInfo; 12 | import org.geoserver.catalog.FeatureTypeCallback; 13 | import org.geotools.data.DataAccess; 14 | import org.opengis.feature.Feature; 15 | import org.opengis.feature.type.FeatureType; 16 | import org.opengis.feature.type.Name; 17 | 18 | /** 19 | * 20 | * Implementation of FeatureTypeInitializer extension point to initialize 21 | * Elasticsearch datastore. 22 | * 23 | * @see FeatureTypeCallback 24 | * 25 | */ 26 | class ElasticFeatureTypeCallback implements FeatureTypeCallback { 27 | 28 | @Override 29 | public boolean canHandle(FeatureTypeInfo info, 30 | DataAccess dataAccess) { 31 | return dataAccess instanceof ElasticDataStore; 32 | } 33 | 34 | @Override 35 | public boolean initialize(FeatureTypeInfo info, 36 | DataAccess dataAccess, Name temporaryName) { 37 | 38 | ElasticLayerConfiguration layerConfig; 39 | layerConfig = (ElasticLayerConfiguration) info.getMetadata().get(KEY); 40 | if (layerConfig == null) { 41 | layerConfig = new ElasticLayerConfiguration(info.getName()); 42 | } 43 | 44 | ((ElasticDataStore) dataAccess).setLayerConfiguration(layerConfig); 45 | 46 | return false; 47 | } 48 | 49 | @Override 50 | public void dispose(FeatureTypeInfo info, 51 | DataAccess dataAccess, Name temporaryName) { 52 | final ElasticLayerConfiguration layerConfig = (ElasticLayerConfiguration) info.getMetadata().get(KEY); 53 | if (layerConfig != null) { 54 | layerConfig.getAttributes().stream() 55 | .filter(attr -> attr.getName().equals(info.getName())) 56 | .findFirst() 57 | .ifPresent(attribute -> layerConfig.getAttributes().remove(attribute)); 58 | ((ElasticDataStore) dataAccess).getDocTypes().remove(info.getQualifiedName()); 59 | } 60 | } 61 | 62 | @Override 63 | public void flush(FeatureTypeInfo info, 64 | DataAccess dataAccess) { 65 | // nothing to do 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/src/main/java/mil/nga/giat/elasticsearch/ElasticXStreamInitializer.java: -------------------------------------------------------------------------------- 1 | /* (c) 2014 Open Source Geospatial Foundation - all rights reserved 2 | * (c) 2014 OpenPlans 3 | * This code is licensed under the GPL 2.0 license, available at the root 4 | * application directory. 5 | */ 6 | 7 | package mil.nga.giat.elasticsearch; 8 | 9 | import mil.nga.giat.data.elasticsearch.ElasticAttribute; 10 | import mil.nga.giat.data.elasticsearch.ElasticLayerConfiguration; 11 | 12 | import org.geoserver.config.util.XStreamPersister; 13 | import org.geoserver.config.util.XStreamPersisterInitializer; 14 | 15 | import com.thoughtworks.xstream.XStream; 16 | 17 | /** 18 | * 19 | * Implementation of XStreamPersisterInitializer extension point to serialize ElasticLayerConfiguration 20 | * 21 | */ 22 | class ElasticXStreamInitializer implements XStreamPersisterInitializer { 23 | 24 | @Override 25 | public void init(XStreamPersister persister) { 26 | persister.registerBreifMapComplexType("elasticLayerConfiguration",ElasticLayerConfiguration.class); 27 | XStream xs = persister.getXStream(); 28 | xs.alias("esAttribute", ElasticAttribute.class); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/src/main/java/mil/nga/giat/elasticsearch/ElasticXStreamPersisterInitializer.java: -------------------------------------------------------------------------------- 1 | package mil.nga.giat.elasticsearch; 2 | 3 | import org.geoserver.config.util.XStreamPersister; 4 | import org.geoserver.config.util.XStreamPersisterInitializer; 5 | 6 | class ElasticXStreamPersisterInitializer implements XStreamPersisterInitializer { 7 | 8 | @Override 9 | public void init(XStreamPersister persister) { 10 | persister.getXStream().allowTypes(new String[] { 11 | "mil.nga.giat.data.elasticsearch.ElasticAttribute" 12 | }); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /gs-web-elasticsearch/src/main/resources/GeoServerApplication.properties: -------------------------------------------------------------------------------- 1 | # suppress inspection "UnusedProperty" for whole file 2 | data.resource.config.elasticsearch = Elasticsearch 3 | ElasticConfigurationPanel.modalTitle = Elasticsearch fields configuration 4 | ElasticConfigurationPanel.edit = Configure Elasticsearch fields 5 | ElasticConfigurationPage.attributes = Attributes 6 | ElasticConfigurationPage.th.name = Name 7 | ElasticConfigurationPage.th.type = Type 8 | ElasticConfigurationPage.th.use = Use 9 | ElasticConfigurationPage.th.geometry = Geometry 10 | ElasticConfigurationPage.th.srid = SRID 11 | ElasticConfigurationPage.th.defaultGeometry = Default Geometry 12 | ElasticConfigurationPage.th.dateFormat = Date Format 13 | ElasticConfigurationPage.th.analyzed = Analyzed 14 | ElasticConfigurationPage.th.stored = Stored 15 | ElasticConfigurationPage.th.order = Order 16 | ElasticConfigurationPage.th.customName = Custom Name 17 | ElasticConfigurationPage.useAll = Use all 18 | ElasticConfigurationPage.useShortName = Short names 19 | ElasticConfigurationPage.es_save = Apply 20 | ElasticConfigurationPage.es_cancel = Cancel 21 | ElasticConfigurationPage.creationFailure = Creation failure 22 | ElasticConfigurationPage.geomEmptyFailure = Select field for geometry 23 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/LICENSE: -------------------------------------------------------------------------------- 1 | This module is licensed under the terms of the GNU Lesser General Public 2 | License (LGPL), version 2 or later. The directory containing this file should 3 | also contain a copy of the LGPL, as a file named LGPL. 4 | 5 | This library is distributed in the hope that it will be useful, 6 | but WITHOUT ANY WARRANTY; without even the implied warranty of 7 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 8 | Lesser General Public License for more details. 9 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | elasticgeo 6 | mil.nga.giat 7 | 2.16-SNAPSHOT 8 | 9 | gt-elasticsearch-process 10 | 2.16-SNAPSHOT 11 | jar 12 | GeoTools Elasticsearch Processes 13 | 14 | 15 | com.fasterxml.jackson.core 16 | jackson-core 17 | ${jackson.version} 18 | 19 | 20 | com.fasterxml.jackson.core 21 | jackson-databind 22 | ${jackson.version} 23 | 24 | 25 | com.github.davidmoten 26 | geo 27 | 0.7.4 28 | 29 | 30 | 31 | org.geotools 32 | gt-process 33 | ${geotools.version} 34 | provided 35 | 36 | 37 | org.geoserver.extension 38 | gs-wps-core 39 | ${geoserver.version} 40 | provided 41 | 42 | 43 | org.geotools 44 | gt-grid 45 | ${geotools.version} 46 | provided 47 | 48 | 49 | org.geotools 50 | gt-process-feature 51 | ${geotools.version} 52 | provided 53 | 54 | 55 | org.geotools 56 | gt-sample-data 57 | ${geotools.version} 58 | test 59 | 60 | 61 | org.geotools 62 | gt-property 63 | ${geotools.version} 64 | test 65 | 66 | 67 | org.geotools 68 | gt-epsg-hsql 69 | ${geotools.version} 70 | test 71 | 72 | 73 | junit 74 | junit 75 | 4.11 76 | test 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/main/java/mil/nga/giat/process/elasticsearch/BBOXRemovingFilterVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import org.geotools.filter.visitor.DuplicatingFilterVisitor; 8 | import org.opengis.filter.Filter; 9 | import org.opengis.filter.spatial.BBOX; 10 | 11 | class BBOXRemovingFilterVisitor extends DuplicatingFilterVisitor { 12 | 13 | private String geometryPropertyName; 14 | 15 | @Override 16 | public Object visit(BBOX filter, Object extraData) { 17 | geometryPropertyName = filter.getExpression1().toString(); 18 | return Filter.INCLUDE; 19 | } 20 | 21 | public String getGeometryPropertyName() { 22 | return geometryPropertyName; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/main/java/mil/nga/giat/process/elasticsearch/BasicGeoHashGrid.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import java.util.Map; 8 | 9 | public class BasicGeoHashGrid extends GeoHashGrid { 10 | 11 | @Override 12 | public Number computeCellValue(Map bucket) { 13 | return super.pluckDocCount(bucket); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/main/java/mil/nga/giat/process/elasticsearch/GeoHashGridProcess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import org.geotools.coverage.grid.GridCoverage2D; 11 | import org.geotools.coverage.processing.Operations; 12 | import org.geotools.data.Query; 13 | import org.geotools.data.simple.SimpleFeatureCollection; 14 | import org.geotools.factory.CommonFactoryFinder; 15 | import org.geotools.filter.visitor.SimplifyingFilterVisitor; 16 | import org.geotools.geometry.jts.ReferencedEnvelope; 17 | import org.geotools.process.ProcessException; 18 | import org.geotools.process.factory.DescribeParameter; 19 | import org.geotools.process.factory.DescribeProcess; 20 | import org.geotools.process.factory.DescribeResult; 21 | import org.geotools.process.vector.VectorProcess; 22 | import org.geotools.referencing.crs.DefaultGeographicCRS; 23 | import org.opengis.coverage.grid.GridGeometry; 24 | import org.opengis.filter.Filter; 25 | import org.opengis.filter.FilterFactory; 26 | import org.opengis.filter.expression.PropertyName; 27 | import org.opengis.filter.spatial.BBOX; 28 | import org.opengis.util.ProgressListener; 29 | 30 | @SuppressWarnings("unused") 31 | @DescribeProcess(title = "geoHashGridAgg", description = "Computes a grid from GeoHash grid aggregation buckets with values corresponding to doc_count values.") 32 | public class GeoHashGridProcess implements VectorProcess { 33 | 34 | private final static FilterFactory FILTER_FACTORY = CommonFactoryFinder.getFilterFactory(null); 35 | 36 | public enum Strategy { 37 | 38 | BASIC(BasicGeoHashGrid.class), 39 | METRIC(MetricGeoHashGrid.class), 40 | NESTED_AGG(NestedAggGeoHashGrid.class); 41 | 42 | private final Class clazz; 43 | 44 | Strategy(Class clazz) { 45 | this.clazz = clazz; 46 | } 47 | 48 | GeoHashGrid createNewInstance() throws ReflectiveOperationException { 49 | return clazz.getConstructor().newInstance(); 50 | } 51 | 52 | } 53 | 54 | @DescribeResult(description = "Output raster") 55 | public GridCoverage2D execute( 56 | 57 | // process data 58 | @DescribeParameter(name = "data", description = "Input features") SimpleFeatureCollection obsFeatures, 59 | 60 | // process parameters 61 | @DescribeParameter(name = "pixelsPerCell", description = "Resolution used for upsampling (in pixels)", defaultValue="1", min = 1) Integer argPixelsPerCell, 62 | @DescribeParameter(name = "gridStrategy", description = "GeoHash grid strategy", defaultValue="Basic", min = 1) String gridStrategy, 63 | @DescribeParameter(name = "gridStrategyArgs", description = "Grid strategy arguments", min = 0) List gridStrategyArgs, 64 | @DescribeParameter(name = "emptyCellValue", description = "Default cell value", min = 0) Float emptyCellValue, 65 | @DescribeParameter(name = "scaleMin", description = "Scale minimum", defaultValue="0") Float scaleMin, 66 | @DescribeParameter(name = "scaleMax", description = "Scale maximum", min = 0) Float scaleMax, 67 | @DescribeParameter(name = "useLog", description = "Whether to use log values (default=false)", defaultValue="false") Boolean useLog, 68 | 69 | // output image parameters 70 | @DescribeParameter(name = "outputBBOX", description = "Bounding box of the output") ReferencedEnvelope argOutputEnv, 71 | @DescribeParameter(name = "outputWidth", description = "Width of output raster in pixels") Integer argOutputWidth, 72 | @DescribeParameter(name = "outputHeight", description = "Height of output raster in pixels") Integer argOutputHeight, 73 | 74 | ProgressListener monitor) throws ProcessException { 75 | 76 | try { 77 | // construct and populate grid 78 | final GeoHashGrid geoHashGrid = Strategy.valueOf(gridStrategy.toUpperCase()).createNewInstance(); 79 | geoHashGrid.setParams(gridStrategyArgs); 80 | geoHashGrid.setEmptyCellValue(emptyCellValue); 81 | geoHashGrid.setScale(new RasterScale(scaleMin, scaleMax, useLog)); 82 | geoHashGrid.initalize(argOutputEnv, obsFeatures); 83 | // convert to grid coverage 84 | final GridCoverage2D nativeCoverage = geoHashGrid.toGridCoverage2D(); 85 | 86 | // reproject 87 | final GridCoverage2D transformedCoverage = (GridCoverage2D) Operations.DEFAULT.resample(nativeCoverage, argOutputEnv.getCoordinateReferenceSystem()); 88 | // upscale to approximate output resolution 89 | final GridCoverage2D scaledCoverage = GridCoverageUtil.scale(transformedCoverage, argOutputWidth*argPixelsPerCell, argOutputHeight*argPixelsPerCell); 90 | // crop (geohash grid envelope will always contain output bbox) 91 | final GridCoverage2D croppedCoverage = GridCoverageUtil.crop(scaledCoverage, argOutputEnv); 92 | return GridCoverageUtil.scale(croppedCoverage, argOutputWidth, argOutputHeight); 93 | } catch (Exception e) { 94 | throw new ProcessException("Error executing GeoHashGridProcess", e); 95 | } 96 | } 97 | 98 | public Query invertQuery( 99 | @DescribeParameter(name = "outputBBOX", description = "Georeferenced bounding box of the output") ReferencedEnvelope envelope, 100 | Query targetQuery, GridGeometry targetGridGeometry 101 | ) throws ProcessException { 102 | 103 | final BBOXRemovingFilterVisitor visitor = new BBOXRemovingFilterVisitor(); 104 | Filter filter = (Filter) targetQuery.getFilter().accept(visitor, null); 105 | final String geometryName = visitor.getGeometryPropertyName(); 106 | if (geometryName != null) { 107 | final BBOX bbox; 108 | try { 109 | if (envelope.getCoordinateReferenceSystem() != null) { 110 | envelope = envelope.transform(DefaultGeographicCRS.WGS84,false); 111 | } 112 | bbox = FILTER_FACTORY.bbox(geometryName, envelope.getMinX(), envelope.getMinY(), envelope.getMaxX(), envelope.getMaxY(), "EPSG:4326"); 113 | } catch (Exception e) { 114 | throw new ProcessException("Unable to create bbox filter for feature source", e); 115 | } 116 | filter = (Filter) FILTER_FACTORY.and(filter, bbox).accept(new SimplifyingFilterVisitor(), null); 117 | targetQuery.setFilter(filter); 118 | } 119 | 120 | final List properties = new ArrayList<>(); 121 | properties.add(FILTER_FACTORY.property("_aggregation")); 122 | targetQuery.setProperties(properties); 123 | return targetQuery; 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/main/java/mil/nga/giat/process/elasticsearch/GridCell.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | class GridCell { 8 | 9 | private final String geohash; 10 | 11 | private final Number value; 12 | 13 | public GridCell(String geohash, Number value) { 14 | this.geohash = geohash; 15 | this.value = value; 16 | } 17 | 18 | public String getGeohash() { 19 | return geohash; 20 | } 21 | 22 | public Number getValue() { 23 | return value; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/main/java/mil/nga/giat/process/elasticsearch/GridCoverageUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import java.awt.image.Raster; 8 | import java.awt.image.RenderedImage; 9 | 10 | import org.geotools.coverage.grid.GridCoverage2D; 11 | import org.geotools.coverage.processing.CoverageProcessor; 12 | import org.geotools.coverage.processing.Operations; 13 | import org.geotools.geometry.GeneralEnvelope; 14 | import org.opengis.geometry.Envelope; 15 | import org.opengis.parameter.ParameterValueGroup; 16 | 17 | class GridCoverageUtil { 18 | 19 | public static GridCoverage2D scale(GridCoverage2D coverage, float width, float height) { 20 | final RenderedImage renderedImage = coverage.getRenderedImage(); 21 | final Raster renderedGrid = renderedImage.getData(); 22 | float yScale = width/renderedGrid.getWidth(); 23 | float xScale = height/renderedGrid.getHeight(); 24 | 25 | final Operations ops = new Operations(null); 26 | return (GridCoverage2D) ops.scale(coverage, xScale, yScale, 0, 0); 27 | } 28 | 29 | public static GridCoverage2D crop(GridCoverage2D coverage, Envelope envelope) { 30 | final CoverageProcessor processor = new CoverageProcessor(); 31 | 32 | final ParameterValueGroup param = processor.getOperation("CoverageCrop").getParameters(); 33 | 34 | final GeneralEnvelope crop = new GeneralEnvelope(envelope); 35 | param.parameter("Source").setValue( coverage ); 36 | param.parameter("Envelope").setValue( crop ); 37 | 38 | return (GridCoverage2D) processor.doOperation(param); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/main/java/mil/nga/giat/process/elasticsearch/MetricGeoHashGrid.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class MetricGeoHashGrid extends GeoHashGrid { 11 | 12 | public final static String DEFAULT_METRIC_KEY = "metric"; 13 | 14 | private final static int METRIC_KEY_INDEX = 0; 15 | 16 | private final static int VALUE_KEY_INDEX = 1; 17 | 18 | private String metricKey = DEFAULT_METRIC_KEY; 19 | 20 | private String valueKey = GeoHashGrid.VALUE_KEY; 21 | 22 | @Override 23 | public void setParams(List params) { 24 | if (null != params) { 25 | if (params.size() >= 1) { 26 | metricKey = params.get(METRIC_KEY_INDEX); 27 | } 28 | if (params.size() >= 2) { 29 | valueKey = params.get(VALUE_KEY_INDEX); 30 | } 31 | } 32 | } 33 | 34 | @Override 35 | public Number computeCellValue(Map bucket) { 36 | return super.pluckMetricValue(bucket, metricKey, valueKey); 37 | } 38 | 39 | public String getMetricKey() { 40 | return metricKey; 41 | } 42 | 43 | public String getValueKey() { 44 | return valueKey; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/main/java/mil/nga/giat/process/elasticsearch/NestedAggGeoHashGrid.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.logging.Logger; 11 | 12 | import org.geotools.util.logging.Logging; 13 | 14 | public class NestedAggGeoHashGrid extends GeoHashGrid { 15 | 16 | private final static Logger LOGGER = Logging.getLogger(NestedAggGeoHashGrid.class); 17 | 18 | private final static int NESTED_KEY_INDEX = 0; 19 | 20 | private final static int METRIC_KEY_INDEX = 1; 21 | 22 | private final static int VALUE_KEY_INDEX = 2; 23 | 24 | private final static int SELECTION_STRATEGY_INDEX = 3; 25 | 26 | private final static int RASTER_STRATEGY_INDEX = 4; 27 | 28 | private final static int TERMS_MAP_INDEX = 5; 29 | 30 | final static String SELECT_LARGEST = "largest"; 31 | 32 | final static String SELECT_SMALLEST = "smallest"; 33 | 34 | final static String RASTER_FROM_VALUE = "value"; 35 | 36 | final static String RASTER_FROM_KEY = "key"; 37 | 38 | final static String DEFAULT_AGG_KEY = "nested"; 39 | 40 | final static String DEFAULT_METRIC_KEY = ""; 41 | 42 | private String nestedAggKey = DEFAULT_AGG_KEY; 43 | 44 | private String metricKey = DEFAULT_METRIC_KEY; 45 | 46 | private String valueKey = GeoHashGrid.VALUE_KEY; 47 | 48 | private String selectionStrategy = SELECT_LARGEST; 49 | 50 | private String rasterStrategy = RASTER_FROM_VALUE; 51 | 52 | private Map termsMap = null; 53 | 54 | @Override 55 | public void setParams(List params) { 56 | if (null != params) { 57 | if (params.size() < 5) { 58 | LOGGER.warning("Parameters list does not contain required length; you provided " + params.size() + ", expecting: 5 or more"); 59 | throw new IllegalArgumentException(); 60 | } 61 | nestedAggKey = params.get(NESTED_KEY_INDEX); 62 | metricKey = params.get(METRIC_KEY_INDEX); 63 | valueKey = params.get(VALUE_KEY_INDEX); 64 | switch (params.get(SELECTION_STRATEGY_INDEX)) { 65 | case SELECT_SMALLEST: 66 | selectionStrategy = params.get(SELECTION_STRATEGY_INDEX); 67 | break; 68 | case SELECT_LARGEST: 69 | selectionStrategy = params.get(SELECTION_STRATEGY_INDEX); 70 | break; 71 | default: 72 | LOGGER.warning("Unexpected buckets selection strategy parameter; you provided " + params.get(SELECTION_STRATEGY_INDEX) + ", defaulting to: " + selectionStrategy); 73 | } 74 | switch (params.get(RASTER_STRATEGY_INDEX)) { 75 | case RASTER_FROM_VALUE: 76 | rasterStrategy = params.get(RASTER_STRATEGY_INDEX); 77 | break; 78 | case RASTER_FROM_KEY: 79 | rasterStrategy = params.get(RASTER_STRATEGY_INDEX); 80 | break; 81 | default: 82 | LOGGER.warning("Unexpected raster strategy parameter; you provided " + params.get(RASTER_STRATEGY_INDEX) + ", defaulting to: " + rasterStrategy); 83 | } 84 | if (rasterStrategy.equals(RASTER_FROM_KEY) && params.size() >= 6) { 85 | termsMap = new HashMap<>(); 86 | String[] terms = params.get(TERMS_MAP_INDEX).split(";"); 87 | for (String term : terms) { 88 | String[] keyValueSplit = term.split(":"); 89 | if (keyValueSplit.length != 2) { 90 | LOGGER.warning("Term " + term + " does not contain required format :"); 91 | throw new IllegalArgumentException(); 92 | } 93 | termsMap.put(keyValueSplit[0], new Integer(keyValueSplit[1])); 94 | } 95 | } 96 | } 97 | } 98 | 99 | @Override 100 | public Number computeCellValue(Map geogridBucket) { 101 | List> aggBuckets = super.pluckAggBuckets(geogridBucket, nestedAggKey); 102 | Number rasterValue = 0; 103 | switch (selectionStrategy) { 104 | case SELECT_SMALLEST: 105 | rasterValue = selectSmallest(aggBuckets); 106 | break; 107 | case SELECT_LARGEST: 108 | rasterValue = selectLargest(aggBuckets); 109 | break; 110 | } 111 | return rasterValue; 112 | } 113 | 114 | Number selectLargest(List> buckets) { 115 | String largestKey = pluckBucketName(buckets.get(0)); 116 | Number largestValue = super.pluckMetricValue(buckets.get(0), metricKey, valueKey); 117 | for (Map bucket : buckets) { 118 | Number value = super.pluckMetricValue(bucket, metricKey, valueKey); 119 | if (value.doubleValue() > largestValue.doubleValue()) { 120 | largestKey = super.pluckBucketName(bucket); 121 | largestValue = value; 122 | } 123 | } 124 | return bucketToRaster(largestKey, largestValue); 125 | } 126 | 127 | Number selectSmallest(List> buckets) { 128 | String smallestKey = pluckBucketName(buckets.get(0)); 129 | Number smallestValue = super.pluckMetricValue(buckets.get(0), metricKey, valueKey); 130 | for (Map bucket : buckets) { 131 | Number value = super.pluckMetricValue(bucket, metricKey, valueKey); 132 | if (value.doubleValue() < smallestValue.doubleValue()) { 133 | smallestKey = super.pluckBucketName(bucket); 134 | smallestValue = value; 135 | } 136 | } 137 | return bucketToRaster(smallestKey, smallestValue); 138 | } 139 | 140 | Number bucketToRaster(String key, Number value) { 141 | Number rasterValue = value; 142 | if (rasterStrategy.equals(RASTER_FROM_KEY)) { 143 | if (null != termsMap) { 144 | if (termsMap.containsKey(key)) { 145 | rasterValue = termsMap.get(key); 146 | } else { 147 | LOGGER.warning("Cannot convert key (String) to raster value, mapping does not contain key " + key + ". Add key to terms_map argument to resolve."); 148 | throw new IllegalArgumentException(); 149 | } 150 | } else { 151 | try { 152 | rasterValue = Double.valueOf(key); 153 | } catch (NumberFormatException nfe) { 154 | LOGGER.warning("Cannot convert key (String) to raster value, key, " + key + ", is not a number. Use terms_map argument to map Strings to Numbers."); 155 | throw new IllegalArgumentException(); 156 | } 157 | } 158 | } 159 | return rasterValue; 160 | } 161 | 162 | public String getNestedAggKey() { 163 | return nestedAggKey; 164 | } 165 | 166 | public String getMetricKey() { 167 | return metricKey; 168 | } 169 | 170 | public String getValueKey() { 171 | return valueKey; 172 | } 173 | 174 | public String getSelectionStrategy() { 175 | return selectionStrategy; 176 | } 177 | 178 | public String getRasterStrategy() { 179 | return rasterStrategy; 180 | } 181 | 182 | public Map getTermsMap() { 183 | return termsMap; 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/main/java/mil/nga/giat/process/elasticsearch/RasterScale.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | class RasterScale { 8 | 9 | private static final float DEFAULT_SCALE_MIN = 0f; 10 | 11 | private final Float scaleMin; 12 | 13 | private final Float scaleMax; 14 | 15 | private final boolean scaleLog; 16 | 17 | private Float dataMin; 18 | 19 | private Float dataMax; 20 | 21 | public RasterScale() { 22 | this(null, null, false); 23 | } 24 | 25 | public RasterScale(boolean useLog) { 26 | this(null, null, useLog); 27 | } 28 | 29 | public RasterScale(Float scaleMax) { 30 | this(DEFAULT_SCALE_MIN, scaleMax, false); 31 | } 32 | 33 | public RasterScale(Float scaleMin, Float scaleMax) { 34 | this(scaleMin, scaleMax, false); 35 | } 36 | 37 | public RasterScale(Float scaleMin, Float scaleMax, boolean scaleLog) { 38 | this.scaleMin = scaleMin; 39 | this.scaleMax = scaleMax; 40 | this.scaleLog = scaleLog; 41 | if (scaleMax != null && (scaleMin == null || scaleMax.floatValue() == scaleMin)) { 42 | throw new IllegalArgumentException(); 43 | } 44 | } 45 | 46 | public float scaleValue(float value) { 47 | if (scaleLog && value > 0) { 48 | value = (float) Math.log10(value); 49 | } 50 | if (scaleMax == null) { 51 | return value; 52 | } else if (dataMax.floatValue() == dataMin) { 53 | return scaleMax; 54 | } else { 55 | return ((scaleMax - scaleMin) * (value - dataMin) / (dataMax - dataMin)) + scaleMin; 56 | } 57 | } 58 | 59 | public void prepareScale(float value) { 60 | if (scaleLog && value > 0) { 61 | value = (float) Math.log10(value); 62 | } 63 | if (scaleMax != null && dataMin != null) { 64 | if (value < dataMin) { 65 | dataMin = value; 66 | } 67 | if (value > dataMax) { 68 | dataMax = value; 69 | } 70 | } else if (scaleMax != null) { 71 | dataMin = value; 72 | dataMax = value; 73 | } 74 | } 75 | 76 | public boolean isScaleSet() { 77 | return scaleMax != null; 78 | } 79 | 80 | public Float getScaleMin() { 81 | return scaleMin; 82 | } 83 | 84 | public Float getScaleMax() { 85 | return scaleMax; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/main/resources/META-INF/services/org.geotools.process.ProcessFactory: -------------------------------------------------------------------------------- 1 | org.geotools.process.vector.VectorProcessFactory 2 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/main/resources/META-INF/services/org.geotools.process.vector.VectorProcess: -------------------------------------------------------------------------------- 1 | mil.nga.giat.process.elasticsearch.GeoHashGridProcess 2 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/test/java/mil/nga/giat/process/elasticsearch/GeoHashGridProcessTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | import java.awt.geom.Point2D; 10 | 11 | import org.geotools.coverage.grid.GridCoverage2D; 12 | import org.geotools.data.Query; 13 | import org.geotools.data.simple.SimpleFeatureCollection; 14 | import org.geotools.factory.CommonFactoryFinder; 15 | import org.geotools.geometry.jts.ReferencedEnvelope; 16 | import org.geotools.referencing.CRS; 17 | import org.geotools.referencing.crs.DefaultGeographicCRS; 18 | import org.junit.Before; 19 | import org.junit.Test; 20 | import org.locationtech.jts.geom.Envelope; 21 | import org.opengis.filter.Filter; 22 | import org.opengis.filter.FilterFactory; 23 | import org.opengis.referencing.crs.CRSAuthorityFactory; 24 | import org.opengis.referencing.crs.CoordinateReferenceSystem; 25 | 26 | import com.fasterxml.jackson.core.JsonProcessingException; 27 | import com.fasterxml.jackson.databind.ObjectMapper; 28 | import com.github.davidmoten.geo.GeoHash; 29 | import com.github.davidmoten.geo.LatLong; 30 | import com.google.common.collect.ImmutableList; 31 | import com.google.common.collect.ImmutableMap; 32 | 33 | public class GeoHashGridProcessTest { 34 | 35 | private SimpleFeatureCollection features; 36 | 37 | private double fineDelta; 38 | 39 | private GeoHashGridProcess process; 40 | 41 | private FilterFactory ff; 42 | 43 | @Before 44 | public void setup() throws JsonProcessingException { 45 | ObjectMapper mapper = new ObjectMapper(); 46 | features = TestUtil.createAggregationFeatures(ImmutableList.of( 47 | ImmutableMap.of("_aggregation", mapper.writeValueAsBytes(ImmutableMap.of("key",GeoHash.encodeHash(new LatLong(-89.9,-179.9),1),"doc_count",10))), 48 | ImmutableMap.of("_aggregation", mapper.writeValueAsBytes(ImmutableMap.of("key",GeoHash.encodeHash(new LatLong(0.1,0.1),1),"doc_count",10))), 49 | ImmutableMap.of("_aggregation", mapper.writeValueAsBytes(ImmutableMap.of("key",GeoHash.encodeHash(new LatLong(89.9,179.9),1),"doc_count",10))) 50 | )); 51 | fineDelta = 0.45; 52 | ff = CommonFactoryFinder.getFilterFactory(null); 53 | process = new GeoHashGridProcess(); 54 | } 55 | 56 | @Test 57 | public void testBasic() { 58 | ReferencedEnvelope envelope = new ReferencedEnvelope(-180,180,-90,90,DefaultGeographicCRS.WGS84); 59 | int width = 8; 60 | int height = 4; 61 | int pixelsPerCell = 1; 62 | String strategy = "Basic"; 63 | Float scaleMin = 0f; 64 | 65 | GridCoverage2D coverage = process.execute(features, pixelsPerCell, strategy, null, null, scaleMin, null, false, envelope, width, height, null); 66 | checkInternal(coverage, fineDelta); 67 | checkEdge(coverage, envelope, fineDelta); 68 | } 69 | 70 | @Test 71 | public void testScaled() { 72 | ReferencedEnvelope envelope = new ReferencedEnvelope(-180,180,-90,90,DefaultGeographicCRS.WGS84); 73 | int width = 16; 74 | int height = 8; 75 | int pixelsPerCell = 1; 76 | String strategy = "Basic"; 77 | Float scaleMin = 0f; 78 | 79 | GridCoverage2D coverage = process.execute(features, pixelsPerCell, strategy, null, null, scaleMin, null, false, envelope, width, height, null); 80 | checkInternal(coverage, fineDelta); 81 | checkEdge(coverage, envelope, fineDelta); 82 | } 83 | 84 | @Test 85 | public void testSubCellCrop() { 86 | ReferencedEnvelope envelope = new ReferencedEnvelope(-168.75,168.75,-78.75,78.75,DefaultGeographicCRS.WGS84); 87 | int width = 16; 88 | int height = 8; 89 | int pixelsPerCell = 1; 90 | String strategy = "Basic"; 91 | Float scaleMin = 0f; 92 | 93 | GridCoverage2D coverage = process.execute(features, pixelsPerCell, strategy, null, null, scaleMin, null, false, envelope, width, height, null); 94 | checkInternal(coverage, fineDelta); 95 | checkEdge(coverage, envelope, fineDelta); 96 | } 97 | 98 | @Test 99 | public void testSubCellCropWithSheer() { 100 | ReferencedEnvelope envelope = new ReferencedEnvelope(-168.75,168.75,-78.75,78.75,DefaultGeographicCRS.WGS84); 101 | int width = 900; 102 | int height = 600; 103 | int pixelsPerCell = 1; 104 | String strategy = "Basic"; 105 | Float scaleMin = 0f; 106 | 107 | GridCoverage2D coverage = process.execute(features, pixelsPerCell, strategy, null, null, scaleMin, null, false, envelope, width, height, null); 108 | checkInternal(coverage, fineDelta); 109 | } 110 | 111 | @Test 112 | public void testInvertQuery() { 113 | Filter filter = ff.bbox("geom", 0, 0, 0, 0, "EPSG:4326"); 114 | ReferencedEnvelope env = new ReferencedEnvelope(0,1,2,3,DefaultGeographicCRS.WGS84); 115 | Query query = new Query(); 116 | query.setFilter(filter); 117 | Query queryOut = process.invertQuery(env, query, null); 118 | assertEquals(ff.bbox("geom", 0, 2, 1, 3, "EPSG:4326"), queryOut.getFilter()); 119 | } 120 | 121 | @Test 122 | public void testInvertQueryAcrossDateline() { 123 | Filter filter = ff.bbox("geom", 0, 0, 0, 0, "EPSG:4326"); 124 | ReferencedEnvelope env = new ReferencedEnvelope(-179,179,2,3,DefaultGeographicCRS.WGS84); 125 | Query query = new Query(); 126 | query.setFilter(filter); 127 | Query queryOut = process.invertQuery(env, query, null); 128 | assertEquals(ff.bbox("geom", -179, 2, 179, 3, "EPSG:4326"), queryOut.getFilter()); 129 | } 130 | 131 | @Test 132 | public void testInvertQueryNorthEastAxisOrder() throws Exception { 133 | Filter filter = ff.bbox("geom", 0, 0, 0, 0, "EPSG:4326"); 134 | CRSAuthorityFactory factory = CRS.getAuthorityFactory(false); 135 | CoordinateReferenceSystem crs = factory.createCoordinateReferenceSystem("EPSG:4326"); 136 | ReferencedEnvelope env = new ReferencedEnvelope(2,3,0,1,crs); 137 | Query query = new Query(); 138 | query.setFilter(filter); 139 | Query queryOut = process.invertQuery(env, query, null); 140 | assertEquals(ff.bbox("geom", 0, 2, 1, 3, "EPSG:4326"), queryOut.getFilter()); 141 | } 142 | 143 | @Test 144 | public void testInvertQueryWithOtherFilterElement() { 145 | Filter filter = ff.and(ff.equals(ff.property("key"), ff.literal("value")), ff.bbox("geom", 0, 0, 0, 0, "EPSG:4326")); 146 | ReferencedEnvelope env = new ReferencedEnvelope(0,1,2,3,DefaultGeographicCRS.WGS84); 147 | Query query = new Query(); 148 | query.setFilter(filter); 149 | Query queryOut = process.invertQuery(env, query, null); 150 | assertEquals(ff.and(ff.equals(ff.property("key"), ff.literal("value")), ff.bbox("geom", 0, 2, 1, 3, "EPSG:4326")), queryOut.getFilter()); 151 | } 152 | 153 | private void checkInternal(GridCoverage2D coverage, double delta) { 154 | assertEquals(10, coverage.evaluate(new Point2D.Double(-135-delta, -45-delta), new float[1])[0],1e-10); 155 | assertEquals(0, coverage.evaluate(new Point2D.Double(-135+delta, -45+delta), new float[1])[0],1e-10); 156 | 157 | assertEquals(0, coverage.evaluate(new Point2D.Double(-delta, -delta), new float[1])[0],1e-10); 158 | assertEquals(10, coverage.evaluate(new Point2D.Double(delta, delta), new float[1])[0],1e-10); 159 | assertEquals(10, coverage.evaluate(new Point2D.Double(45-delta, 45-delta), new float[1])[0],1e-10); 160 | assertEquals(0, coverage.evaluate(new Point2D.Double(45+delta, 45+delta), new float[1])[0],1e-10); 161 | 162 | assertEquals(10, coverage.evaluate(new Point2D.Double(135+delta, 45+delta), new float[1])[0],1e-10); 163 | assertEquals(0, coverage.evaluate(new Point2D.Double(135-delta, 45-delta), new float[1])[0],1e-10); 164 | } 165 | 166 | private void checkEdge(GridCoverage2D coverage, Envelope env, double delta) { 167 | assertEquals(10, coverage.evaluate(new Point2D.Double(env.getMinX()+delta, env.getMinY()+delta), new float[1])[0],1e-10); 168 | assertEquals(10, coverage.evaluate(new Point2D.Double(env.getMaxX()-delta, env.getMaxY()-delta), new float[1])[0],1e-10); 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/test/java/mil/nga/giat/process/elasticsearch/GridCoverageUtilTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import java.awt.geom.Point2D; 8 | import java.awt.image.RenderedImage; 9 | import java.util.stream.IntStream; 10 | 11 | import org.geotools.coverage.CoverageFactoryFinder; 12 | import org.geotools.coverage.grid.GridCoordinates2D; 13 | import org.geotools.coverage.grid.GridCoverage2D; 14 | import org.geotools.coverage.grid.GridCoverageFactory; 15 | import org.geotools.geometry.jts.ReferencedEnvelope; 16 | import org.geotools.util.factory.GeoTools; 17 | import org.junit.Test; 18 | import org.opengis.geometry.MismatchedDimensionException; 19 | 20 | import static org.junit.Assert.*; 21 | 22 | public class GridCoverageUtilTest { 23 | 24 | @Test 25 | public void testExactUpScale() { 26 | float[][] grid = new float[][] {{1,2},{3,4}}; 27 | final GridCoverageFactory coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(GeoTools.getDefaultHints()); 28 | final GridCoverage2D coverage = coverageFactory.create("geohashGridAgg", grid, new ReferencedEnvelope(0,1,0,1,null)); 29 | GridCoverage2D scaled = GridCoverageUtil.scale(coverage, 4, 4); 30 | final RenderedImage renderedImage = scaled.getRenderedImage(); 31 | assertEquals(4, renderedImage.getWidth()); 32 | assertEquals(4, renderedImage.getHeight()); 33 | float[][] expected = new float[][] {{1,1,2,2},{1,1,2,2},{3,3,4,4},{3,3,4,4}}; 34 | IntStream.range(0,4).forEach(i->IntStream.range(0, 4).forEach(j -> { 35 | float actual = scaled.evaluate(new GridCoordinates2D(j,i), new float[1])[0]; 36 | assertEquals(expected[i][j], actual, 1e-10); 37 | })); 38 | } 39 | 40 | @Test 41 | public void testExactDownScale() { 42 | float[][] grid = new float[][] {{1,1,2,2},{1,1,2,2},{3,3,4,4},{3,3,4,4}}; 43 | final GridCoverageFactory coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(GeoTools.getDefaultHints()); 44 | final GridCoverage2D coverage = coverageFactory.create("geohashGridAgg", grid, new ReferencedEnvelope(0,1,0,1,null)); 45 | GridCoverage2D scaled = GridCoverageUtil.scale(coverage, 2,2); 46 | final RenderedImage renderedImage = scaled.getRenderedImage(); 47 | assertEquals(2, renderedImage.getWidth()); 48 | assertEquals(2, renderedImage.getHeight()); 49 | float[][] expected = new float[][] {{1,2},{3,4}}; 50 | IntStream.range(0,2).forEach(i->IntStream.range(0, 2).forEach(j -> { 51 | float actual = scaled.evaluate(new GridCoordinates2D(j,i), new float[1])[0]; 52 | assertEquals(expected[i][j], actual, 1e-10); 53 | })); 54 | } 55 | 56 | @Test 57 | public void testInexactScale() { 58 | float[][] grid = new float[][] {{1,2},{3,4}}; 59 | final GridCoverageFactory coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(GeoTools.getDefaultHints()); 60 | final GridCoverage2D coverage = coverageFactory.create("geohashGridAgg", grid, new ReferencedEnvelope(0,1,0,1,null)); 61 | GridCoverage2D scaled = GridCoverageUtil.scale(coverage, 3, 7); 62 | final RenderedImage renderedImage = scaled.getRenderedImage(); 63 | assertEquals(7, renderedImage.getWidth()); 64 | assertEquals(3, renderedImage.getHeight()); 65 | } 66 | 67 | @Test 68 | public void testSmallScale() { 69 | float[][] grid = new float[1500][1500]; 70 | final GridCoverageFactory coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(GeoTools.getDefaultHints()); 71 | final GridCoverage2D coverage = coverageFactory.create("geohashGridAgg", grid, new ReferencedEnvelope(0,1,0,1,null)); 72 | GridCoverage2D scaled = GridCoverageUtil.scale(coverage, 1501, 1499); 73 | final RenderedImage renderedImage = scaled.getRenderedImage(); 74 | assertEquals(1499, renderedImage.getWidth()); 75 | assertEquals(1501, renderedImage.getHeight()); 76 | } 77 | 78 | @Test 79 | public void testLargeScale() { 80 | float[][] grid = new float[2][2]; 81 | final GridCoverageFactory coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(GeoTools.getDefaultHints()); 82 | final GridCoverage2D coverage = coverageFactory.create("geohashGridAgg", grid, new ReferencedEnvelope(0,1,0,1,null)); 83 | GridCoverage2D scaled = GridCoverageUtil.scale(coverage, 1501, 1499); 84 | final RenderedImage renderedImage = scaled.getRenderedImage(); 85 | assertEquals(1499, renderedImage.getWidth()); 86 | assertEquals(1501, renderedImage.getHeight()); 87 | } 88 | 89 | @Test 90 | public void testCrop() throws MismatchedDimensionException { 91 | float[][] grid = new float[][] {{3,4},{1,2}}; 92 | final GridCoverageFactory coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(GeoTools.getDefaultHints()); 93 | final GridCoverage2D coverage = coverageFactory.create("geohashGridAgg", grid, new ReferencedEnvelope(0,20,0,20,null)); 94 | final ReferencedEnvelope envelope = new ReferencedEnvelope(10,20,10,20,null); 95 | final GridCoverage2D croppedCoverage = GridCoverageUtil.crop(coverage, envelope); 96 | final RenderedImage renderedImage = croppedCoverage.getRenderedImage(); 97 | assertEquals(1, renderedImage.getWidth()); 98 | assertEquals(1, renderedImage.getHeight()); 99 | assertEquals(4, croppedCoverage.evaluate(new Point2D.Double(15,15), new float[1])[0], 1e-10); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/test/java/mil/nga/giat/process/elasticsearch/MetricGeoHashGridTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | 16 | public class MetricGeoHashGridTest { 17 | 18 | private MetricGeoHashGrid geohashGrid; 19 | 20 | @Before 21 | public void setup() { 22 | this.geohashGrid = new MetricGeoHashGrid(); 23 | } 24 | 25 | @Test 26 | public void testSetParams_defaults() { 27 | geohashGrid.setParams(null); 28 | assertEquals(MetricGeoHashGrid.DEFAULT_METRIC_KEY, geohashGrid.getMetricKey()); 29 | assertEquals(GeoHashGrid.VALUE_KEY, geohashGrid.getValueKey()); 30 | } 31 | 32 | @Test 33 | public void testSetParams() { 34 | String metricKey = "mymetric"; 35 | String valueKey = "myvalue"; 36 | List params = new ArrayList<>(); 37 | params.add(metricKey); 38 | params.add(valueKey); 39 | geohashGrid.setParams(params); 40 | assertEquals(metricKey, geohashGrid.getMetricKey()); 41 | assertEquals(valueKey, geohashGrid.getValueKey()); 42 | } 43 | 44 | @Test 45 | public void testSetParams_justMetric() { 46 | String metricKey = "mymetric"; 47 | List params = new ArrayList<>(); 48 | params.add(metricKey); 49 | geohashGrid.setParams(params); 50 | assertEquals(metricKey, geohashGrid.getMetricKey()); 51 | assertEquals(GeoHashGrid.VALUE_KEY, geohashGrid.getValueKey()); 52 | } 53 | 54 | @Test 55 | public void testComputeCellValue() { 56 | int value = 5; 57 | Map metricBucket = TestUtil.createMetricBucket(1, MetricGeoHashGrid.DEFAULT_METRIC_KEY, GeoHashGrid.VALUE_KEY, value); 58 | Number rasterValue = geohashGrid.computeCellValue(metricBucket); 59 | assertEquals(value, rasterValue); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/test/java/mil/nga/giat/process/elasticsearch/NestedAggGeoHashGridTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | 16 | public class NestedAggGeoHashGridTest { 17 | 18 | private static final int[] AGG_RESULTS = {1, 2, 3, 4, 5}; 19 | 20 | private NestedAggGeoHashGrid geohashGrid; 21 | 22 | @Before 23 | public void setup() { 24 | this.geohashGrid = new NestedAggGeoHashGrid(); 25 | } 26 | 27 | @Test 28 | public void testSetParams_defaults() { 29 | geohashGrid.setParams(null); 30 | assertEquals(NestedAggGeoHashGrid.DEFAULT_AGG_KEY, geohashGrid.getNestedAggKey()); 31 | assertEquals(NestedAggGeoHashGrid.DEFAULT_METRIC_KEY, geohashGrid.getMetricKey()); 32 | assertEquals(GeoHashGrid.VALUE_KEY, geohashGrid.getValueKey()); 33 | assertEquals(NestedAggGeoHashGrid.SELECT_LARGEST, geohashGrid.getSelectionStrategy()); 34 | assertEquals(NestedAggGeoHashGrid.RASTER_FROM_VALUE, geohashGrid.getRasterStrategy()); 35 | assertNull(geohashGrid.getTermsMap()); 36 | } 37 | 38 | @Test 39 | public void testSetParams() { 40 | String aggKey = "myagg"; 41 | String metricKey = "mymetric"; 42 | String valueKey = "myvalue"; 43 | List params = new ArrayList<>(); 44 | params.add(aggKey); 45 | params.add(metricKey); 46 | params.add(valueKey); 47 | params.add(NestedAggGeoHashGrid.SELECT_SMALLEST); 48 | params.add(NestedAggGeoHashGrid.RASTER_FROM_KEY); 49 | params.add("key1:1;key2:2"); 50 | geohashGrid.setParams(params); 51 | assertEquals(aggKey, geohashGrid.getNestedAggKey()); 52 | assertEquals(metricKey, geohashGrid.getMetricKey()); 53 | assertEquals(valueKey, geohashGrid.getValueKey()); 54 | assertEquals(NestedAggGeoHashGrid.SELECT_SMALLEST, geohashGrid.getSelectionStrategy()); 55 | assertEquals(NestedAggGeoHashGrid.RASTER_FROM_KEY, geohashGrid.getRasterStrategy()); 56 | Map termsMap = geohashGrid.getTermsMap(); 57 | assertEquals(2, termsMap.size()); 58 | assertEquals(new Integer(1), termsMap.get("key1")); 59 | assertEquals(new Integer(2), termsMap.get("key2")); 60 | } 61 | 62 | @Test 63 | public void testSetParams_ignoreInvalidParams() { 64 | String aggKey = "myagg"; 65 | String metricKey = "mymetric"; 66 | String valueKey = "myvalue"; 67 | List params = new ArrayList<>(); 68 | params.add(aggKey); 69 | params.add(metricKey); 70 | params.add(valueKey); 71 | params.add("invalid token"); 72 | params.add("invalid token"); 73 | geohashGrid.setParams(params); 74 | assertEquals(aggKey, geohashGrid.getNestedAggKey()); 75 | assertEquals(metricKey, geohashGrid.getMetricKey()); 76 | assertEquals(valueKey, geohashGrid.getValueKey()); 77 | assertEquals(NestedAggGeoHashGrid.SELECT_LARGEST, geohashGrid.getSelectionStrategy()); 78 | assertEquals(NestedAggGeoHashGrid.RASTER_FROM_VALUE, geohashGrid.getRasterStrategy()); 79 | assertNull(geohashGrid.getTermsMap()); 80 | } 81 | 82 | @Test(expected=IllegalArgumentException.class) 83 | public void testSetParams_notEnoughParameters() { 84 | geohashGrid.setParams(new ArrayList<>()); 85 | } 86 | 87 | @Test 88 | public void testComputeCellValue() { 89 | Number rasterValue = geohashGrid.computeCellValue(TestUtil.createAggBucket(NestedAggGeoHashGrid.DEFAULT_AGG_KEY, AGG_RESULTS)); 90 | assertEquals(5, rasterValue); 91 | } 92 | 93 | @Test 94 | public void testSelectLargest() { 95 | Number rasterValue = geohashGrid.selectLargest(TestUtil.createBuckets(AGG_RESULTS)); 96 | assertEquals(5, rasterValue); 97 | } 98 | 99 | @Test 100 | public void testSelectSmallest() { 101 | Number rasterValue = geohashGrid.selectSmallest(TestUtil.createBuckets(AGG_RESULTS)); 102 | assertEquals(1, rasterValue); 103 | } 104 | 105 | @Test 106 | public void testBucketToRaster_rasterFromValue() { 107 | Number bucketValue = 5.0; 108 | Number rasterValue = geohashGrid.bucketToRaster("bucket_key", bucketValue); 109 | assertEquals(bucketValue, rasterValue); 110 | } 111 | 112 | @Test 113 | public void testBucketToRaster_rasterFromNumericKey() { 114 | List params = new ArrayList<>(); 115 | params.add("aggKey"); 116 | params.add("metricKey"); 117 | params.add("valueKey"); 118 | params.add(NestedAggGeoHashGrid.SELECT_SMALLEST); 119 | params.add(NestedAggGeoHashGrid.RASTER_FROM_KEY); 120 | geohashGrid.setParams(params); 121 | Number rasterValue = geohashGrid.bucketToRaster("1.0", 5.0); 122 | assertEquals(1.0, rasterValue); 123 | } 124 | 125 | @Test(expected=IllegalArgumentException.class) 126 | public void testBucketToRaster_rasterFromNumericKey_keyIsString() { 127 | List params = new ArrayList<>(); 128 | params.add("aggKey"); 129 | params.add("metricKey"); 130 | params.add("valueKey"); 131 | params.add(NestedAggGeoHashGrid.SELECT_SMALLEST); 132 | params.add(NestedAggGeoHashGrid.RASTER_FROM_KEY); 133 | geohashGrid.setParams(params); 134 | geohashGrid.bucketToRaster("I am not a number!", 5.0); 135 | } 136 | 137 | @Test 138 | public void testBucketToRaster_rasterFromStringKey() { 139 | List params = new ArrayList<>(); 140 | params.add("aggKey"); 141 | params.add("metricKey"); 142 | params.add("valueKey"); 143 | params.add(NestedAggGeoHashGrid.SELECT_SMALLEST); 144 | params.add(NestedAggGeoHashGrid.RASTER_FROM_KEY); 145 | params.add("key1:1;key2:2"); 146 | geohashGrid.setParams(params); 147 | Number rasterValue = geohashGrid.bucketToRaster("key1", 5.0); 148 | assertEquals(1, rasterValue); 149 | } 150 | 151 | @Test(expected=IllegalArgumentException.class) 152 | public void testBucketToRaster_rasterFromStringKey_keyNotInMap() { 153 | List params = new ArrayList<>(); 154 | params.add("aggKey"); 155 | params.add("metricKey"); 156 | params.add("valueKey"); 157 | params.add(NestedAggGeoHashGrid.SELECT_SMALLEST); 158 | params.add(NestedAggGeoHashGrid.RASTER_FROM_KEY); 159 | params.add("key1:1;key2:2"); 160 | geohashGrid.setParams(params); 161 | geohashGrid.bucketToRaster("key3", 5.0); 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/test/java/mil/nga/giat/process/elasticsearch/RasterScaleTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | import org.junit.Test; 10 | 11 | public class RasterScaleTest { 12 | 13 | @Test 14 | public void testRasterScale_noScale() { 15 | RasterScale scale = new RasterScale(); 16 | assertFalse(scale.isScaleSet()); 17 | } 18 | 19 | @Test 20 | public void testRasterScale_maxProvided() { 21 | float scaleMax = 10.0f; 22 | RasterScale scale = new RasterScale(scaleMax); 23 | assertTrue(scale.isScaleSet()); 24 | assertEquals(0, scale.getScaleMin(), 0.0); 25 | assertEquals(scaleMax, scale.getScaleMax(), 0.0); 26 | } 27 | 28 | @Test 29 | public void testRasterScale_minMaxProvided() { 30 | float scaleMax = 10.0f; 31 | float scaleMin = 1.0f; 32 | RasterScale scale = new RasterScale(scaleMin, scaleMax); 33 | assertTrue(scale.isScaleSet()); 34 | assertEquals(scaleMin, scale.getScaleMin(), 0.0); 35 | assertEquals(scaleMax, scale.getScaleMax(), 0.0); 36 | } 37 | 38 | @Test(expected=IllegalArgumentException.class) 39 | public void testRasterScale_minMaxSame() { 40 | float scaleMax = 10.0f; 41 | new RasterScale(scaleMax, scaleMax); 42 | } 43 | 44 | @Test 45 | public void testRasterScale_scaleValue() { 46 | float scaleMax = 10.0f; 47 | float scaleMin = 0.0f; 48 | RasterScale scale = new RasterScale(scaleMin, scaleMax); 49 | scale.prepareScale(30); 50 | scale.prepareScale(20); 51 | scale.prepareScale(10); 52 | scale.prepareScale(40); 53 | assertEquals(10, scale.scaleValue(40), 0.0); 54 | assertEquals(5, scale.scaleValue(25), 0.0); 55 | assertEquals(0, scale.scaleValue(10), 0.0); 56 | } 57 | 58 | @Test 59 | public void testRasterScale_scaleValue_emptyScale() { 60 | RasterScale scale = new RasterScale(); 61 | scale.prepareScale(30); 62 | scale.prepareScale(20); 63 | scale.prepareScale(10); 64 | assertEquals(30, scale.scaleValue(30), 0.0); 65 | assertEquals(20, scale.scaleValue(20), 0.0); 66 | assertEquals(10, scale.scaleValue(10), 0.0); 67 | } 68 | 69 | @Test 70 | public void testRasterScale_scaleValue_dataMinAndDataMaxAreTheSame() { 71 | float scaleMax = 10.0f; 72 | float scaleMin = 1.0f; 73 | RasterScale scale = new RasterScale(scaleMin, scaleMax); 74 | scale.prepareScale(30); 75 | assertEquals(10, scale.scaleValue(30), 0.0); 76 | } 77 | 78 | @Test 79 | public void testRasterScale_log() { 80 | RasterScale scale = new RasterScale(true); 81 | assertEquals(1, scale.scaleValue(10), 0.0); 82 | assertEquals(0, scale.scaleValue(1), 0.0); 83 | } 84 | 85 | @Test 86 | public void testRasterScale_logAndScale() { 87 | float scaleMax = 10.0f; 88 | float scaleMin = 0.0f; 89 | RasterScale scale = new RasterScale(scaleMin, scaleMax, true); 90 | scale.prepareScale(10); 91 | scale.prepareScale(1); 92 | assertEquals(10, scale.scaleValue(10), 0.0); 93 | assertEquals(0, scale.scaleValue(1), 0.0); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /gt-elasticsearch-process/src/test/java/mil/nga/giat/process/elasticsearch/TestUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.process.elasticsearch; 6 | 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import org.geotools.data.simple.SimpleFeatureCollection; 13 | import org.geotools.feature.DefaultFeatureCollection; 14 | import org.geotools.feature.simple.SimpleFeatureBuilder; 15 | import org.geotools.feature.simple.SimpleFeatureTypeBuilder; 16 | import org.opengis.feature.simple.SimpleFeatureType; 17 | 18 | class TestUtil { 19 | 20 | public static SimpleFeatureCollection createAggregationFeatures(List> data) { 21 | final SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); 22 | builder.setName( "testType" ); 23 | builder.add("_aggregation", HashMap.class ); 24 | builder.add("aString", String.class ); 25 | final SimpleFeatureType featureType = builder.buildFeatureType(); 26 | final DefaultFeatureCollection collection = new DefaultFeatureCollection(); 27 | final SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType); 28 | data.forEach(item -> { 29 | item.keySet().forEach(key -> featureBuilder.set(key, item.get(key))); 30 | collection.add(featureBuilder.buildFeature(null)); 31 | }); 32 | return collection; 33 | } 34 | 35 | public static Map createDocCountBucket(String bucketName, int docCount) { 36 | Map bucket = new HashMap<>(); 37 | bucket.put(GeoHashGrid.BUCKET_NAME_KEY, bucketName); 38 | bucket.put(GeoHashGrid.DOC_COUNT_KEY, docCount); 39 | return bucket; 40 | } 41 | 42 | public static Map createMetricBucket(int docCount, String metricName, String valueName, int value) { 43 | Map metric = new HashMap<>(); 44 | metric.put(valueName, value); 45 | 46 | Map bucket = createDocCountBucket("grid_cell_name", docCount); 47 | bucket.put(metricName, metric); 48 | 49 | return bucket; 50 | } 51 | 52 | public static List> createBuckets(int[] values) { 53 | List> buckets = new ArrayList<>(); 54 | for (int i=0; i createAggBucket(String aggName, int[] values) { 61 | int totalDocCount = 0; 62 | List> buckets = new ArrayList<>(); 63 | for (int i=0; i aggResults = new HashMap<>(); 69 | aggResults.put(GeoHashGrid.BUCKETS_KEY, buckets); 70 | 71 | Map bucket = createDocCountBucket("grid_cell_name", totalDocCount); 72 | bucket.put(aggName, aggResults); 73 | 74 | return bucket; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /gt-elasticsearch/LICENSE: -------------------------------------------------------------------------------- 1 | This module is licensed under the terms of the GNU Lesser General Public 2 | License (LGPL), version 2 or later. The directory containing this file should 3 | also contain a copy of the LGPL, as a file named LGPL. 4 | 5 | This library is distributed in the hope that it will be useful, 6 | but WITHOUT ANY WARRANTY; without even the implied warranty of 7 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 8 | Lesser General Public License for more details. 9 | -------------------------------------------------------------------------------- /gt-elasticsearch/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | elasticgeo 6 | mil.nga.giat 7 | 2.16-SNAPSHOT 8 | 9 | gt-elasticsearch 10 | 2.16-SNAPSHOT 11 | jar 12 | GeoTools Elasticsearch DataStore 13 | 14 | docker.elastic.co/elasticsearch/elasticsearch 15 | 16 | 17 | 18 | org.springframework.security 19 | spring-security-core 20 | 5.1.5.RELEASE 21 | 22 | 23 | aopalliance 24 | aopalliance 25 | 26 | 27 | provided 28 | 29 | 30 | mil.nga.giat 31 | joda-shaded 32 | ${project.version} 33 | 34 | 35 | joda-time 36 | joda-time 37 | 38 | 39 | 40 | 41 | org.elasticsearch.client 42 | elasticsearch-rest-client 43 | ${es.version} 44 | 45 | 46 | org.locationtech.spatial4j 47 | spatial4j 48 | 0.6 49 | 50 | 51 | com.fasterxml.jackson.core 52 | jackson-core 53 | ${jackson.version} 54 | 55 | 56 | com.fasterxml.jackson.core 57 | jackson-databind 58 | ${jackson.version} 59 | 60 | 61 | com.github.davidmoten 62 | geo 63 | 0.7.4 64 | 65 | 66 | com.google.guava 67 | guava 68 | ${guava.version} 69 | 70 | 71 | log4j 72 | log4j 73 | ${log4j.version} 74 | 75 | 76 | 77 | org.geotools 78 | gt-main 79 | ${geotools.version} 80 | provided 81 | 82 | 83 | org.geotools 84 | gt-epsg-hsql 85 | ${geotools.version} 86 | provided 87 | 88 | 89 | org.geotools 90 | gt-geojson 91 | ${geotools.version} 92 | provided 93 | 94 | 95 | org.geotools 96 | gt-cql 97 | ${geotools.version} 98 | provided 99 | 100 | 101 | org.mockito 102 | mockito-core 103 | 2.7.5 104 | test 105 | 106 | 107 | junit 108 | junit 109 | 4.11 110 | test 111 | 112 | 113 | org.hamcrest 114 | hamcrest-all 115 | 1.3 116 | test 117 | 118 | 119 | 120 | 121 | 122 | true 123 | src/main/resources 124 | 125 | 126 | 127 | 128 | maven-failsafe-plugin 129 | 2.19.1 130 | 131 | ${failsafeArgLine} -Djava.util.logging.config.file=${project.build.directory}/test-classes/test-classes/logging.properties 132 | ${skip.integration.tests} 133 | 134 | 135 | 136 | 137 | integration-test 138 | verify 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | docker 148 | 149 | 150 | !skip.integration.tests 151 | 152 | 153 | 154 | true 155 | 156 | 157 | 158 | 159 | io.fabric8 160 | docker-maven-plugin 161 | 0.18.1 162 | 163 | 164 | 165 | elasticsearch 166 | ${docker.image}:${es.test.version} 167 | 168 | 169 | -Xmx512m -Xms512m 170 | single-node 171 | false 172 | 173 | 174 | 9200:9200 175 | 176 | 177 | 178 | 179 | http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=60s 180 | GET 181 | 200 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | docker-start 191 | pre-integration-test 192 | 193 | start 194 | 195 | 196 | 197 | docker-stop 198 | post-integration-test 199 | 200 | stop 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticAggregation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | @JsonIgnoreProperties(ignoreUnknown = true) 13 | class ElasticAggregation { 14 | 15 | private List> buckets; 16 | 17 | public List> getBuckets() { 18 | return buckets; 19 | } 20 | 21 | public void setBuckets(List> buckets) { 22 | this.buckets = buckets; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "ElasticAggregation[numBuckets=" + 28 | (buckets != null ? buckets.size() : 0) + 29 | "]"; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticAttribute.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.io.Serializable; 8 | import java.util.Objects; 9 | import java.util.regex.Pattern; 10 | 11 | /** 12 | * Class describing and Elasticsearch attribute including name, type and 13 | * optional information on geometry and date types. Also includes an alternative 14 | * short name, if applicable, that can be used instead of the full path both in 15 | * the feature type and backend Elasticsearch queries. 16 | * 17 | */ 18 | public class ElasticAttribute implements Serializable, Comparable { 19 | 20 | public enum ElasticGeometryType { 21 | GEO_POINT, 22 | GEO_SHAPE 23 | } 24 | 25 | private static final Pattern beginLetters = Pattern.compile("^[A-Za-z_].*"); 26 | 27 | private static final long serialVersionUID = 8839579461838862328L; 28 | 29 | private final String name; 30 | 31 | private String shortName; 32 | 33 | private Boolean useShortName; 34 | 35 | private Class type; 36 | 37 | private ElasticGeometryType geometryType; 38 | 39 | private Boolean use; 40 | 41 | private Boolean defaultGeometry; 42 | 43 | private Integer srid; 44 | 45 | private String dateFormat; 46 | 47 | private Boolean analyzed; 48 | 49 | private boolean stored; 50 | 51 | private boolean nested; 52 | 53 | private Integer order; 54 | 55 | private String customName; 56 | 57 | public ElasticAttribute(String name) { 58 | super(); 59 | this.name = name; 60 | this.use = true; 61 | this.defaultGeometry = false; 62 | this.useShortName = false; 63 | this.stored = false; 64 | this.nested = false; 65 | } 66 | 67 | public ElasticAttribute(ElasticAttribute other) { 68 | this.name = other.name; 69 | this.shortName = other.shortName; 70 | this.type = other.type; 71 | this.use = other.use; 72 | this.defaultGeometry = other.defaultGeometry; 73 | this.srid = other.srid; 74 | this.dateFormat = other.dateFormat; 75 | this.useShortName = other.useShortName; 76 | this.geometryType = other.geometryType; 77 | this.analyzed = other.analyzed; 78 | this.stored = other.stored; 79 | this.nested = other.nested; 80 | this.order = other.order; 81 | this.customName = other.customName; 82 | } 83 | 84 | public String getName() { 85 | return name; 86 | } 87 | 88 | public String getShortName() { 89 | return shortName; 90 | } 91 | 92 | public void setShortName(String shortName) { 93 | this.shortName = shortName; 94 | } 95 | 96 | public Boolean getUseShortName() { 97 | return useShortName; 98 | } 99 | 100 | public void setUseShortName(Boolean useShortName) { 101 | this.useShortName = useShortName; 102 | } 103 | 104 | public Class getType() { 105 | return type; 106 | } 107 | 108 | public void setType(Class type) { 109 | this.type = type; 110 | } 111 | 112 | public ElasticGeometryType getGeometryType() { 113 | return geometryType; 114 | } 115 | 116 | public void setGeometryType(ElasticGeometryType geometryType) { 117 | this.geometryType = geometryType; 118 | } 119 | 120 | public Boolean isUse() { 121 | return use; 122 | } 123 | 124 | public void setUse(Boolean use) { 125 | this.use = use; 126 | } 127 | 128 | public Boolean isDefaultGeometry() { 129 | return defaultGeometry; 130 | } 131 | 132 | public void setDefaultGeometry(Boolean defaultGeometry) { 133 | this.defaultGeometry = defaultGeometry; 134 | } 135 | 136 | public Integer getSrid() { 137 | return srid; 138 | } 139 | 140 | public void setSrid(Integer srid) { 141 | this.srid = srid; 142 | } 143 | 144 | public String getDateFormat() { 145 | return dateFormat; 146 | } 147 | 148 | public void setDateFormat(String dateFormat) { 149 | this.dateFormat = dateFormat; 150 | } 151 | 152 | public Boolean getAnalyzed() { 153 | return analyzed; 154 | } 155 | 156 | public void setAnalyzed(Boolean analyzed) { 157 | this.analyzed = analyzed; 158 | } 159 | 160 | public boolean isStored() { 161 | return stored; 162 | } 163 | 164 | public void setStored(boolean stored) { 165 | this.stored = stored; 166 | } 167 | 168 | public boolean isNested() { 169 | return nested; 170 | } 171 | 172 | public void setNested(boolean nested) { 173 | this.nested = nested; 174 | } 175 | 176 | public void setOrder(Integer order) { 177 | this.order = order; 178 | } 179 | 180 | public Integer getOrder() { 181 | return this.order; 182 | } 183 | 184 | public void setCustomName(String name) { 185 | this.customName = normalizeName(name); 186 | } 187 | 188 | public String getCustomName() { 189 | return this.customName; 190 | } 191 | 192 | public String getDisplayName() { 193 | final String displayName; 194 | if (useShortName) { 195 | displayName = shortName; 196 | } else { 197 | displayName = name; 198 | } 199 | return displayName; 200 | } 201 | 202 | @Override 203 | public int hashCode() { 204 | return Objects.hash(name, type, use, defaultGeometry, srid, dateFormat, 205 | useShortName, geometryType, analyzed, stored, nested, order, customName); 206 | } 207 | 208 | @Override 209 | public boolean equals(Object obj) { 210 | boolean equal; 211 | if (obj == null || getClass() != obj.getClass()) { 212 | equal = false; 213 | } else { 214 | ElasticAttribute other = (ElasticAttribute) obj; 215 | equal = Objects.equals(name, other.name); 216 | equal &= Objects.equals(type, other.type); 217 | equal &= Objects.equals(use, other.use); 218 | equal &= Objects.equals(defaultGeometry, other.defaultGeometry); 219 | equal &= Objects.equals(srid, other.srid); 220 | equal &= Objects.equals(dateFormat, other.dateFormat); 221 | equal &= Objects.equals(useShortName, other.useShortName); 222 | equal &= Objects.equals(geometryType, other.geometryType); 223 | equal &= Objects.equals(analyzed, other.analyzed); 224 | equal &= Objects.equals(stored, other.stored); 225 | equal &= Objects.equals(nested, other.nested); 226 | equal &= Objects.equals(order, other.order); 227 | equal &= Objects.equals(customName, other.customName); 228 | } 229 | return equal; 230 | } 231 | 232 | /** 233 | * Implement comparison logic 234 | * @param o is a non-null ElasticAttribute 235 | * @return negative for before, zero for same, positive after 236 | */ 237 | @Override 238 | public int compareTo(@SuppressWarnings("NullableProblems") ElasticAttribute o) { 239 | if (this.order == null) { 240 | return o.order == null ? this.name.compareTo(o.name) : 1; 241 | } 242 | if (o.order == null) { 243 | return -1; 244 | } 245 | int i = this.order.compareTo(o.order); 246 | return i == 0 ? this.name.compareTo(o.name) : i; 247 | } 248 | 249 | /** 250 | * Perform basic update to the given name to make it XML namespace 251 | * compliant. 252 | * 253 | * @param name Raw name 254 | * @return Name that is XML safe 255 | */ 256 | private static String normalizeName (String name) { 257 | 258 | String normalName = name; 259 | 260 | /* XML element naming rules: 261 | * 1. Element names must start with a letter or underscore 262 | * 2. Element names cannot start with the letters xml 263 | * 3. Element names cannot contain spaces 264 | */ 265 | if (normalName.toLowerCase().startsWith("xml")) { 266 | normalName = "_".concat(normalName); 267 | } else if (! beginLetters.matcher(normalName).matches()) { 268 | normalName = "_".concat(normalName); 269 | } 270 | /* Simply replace all spaces in the name with "_". */ 271 | return normalName.replaceAll(" ", "_"); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticCapabilities.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import org.geotools.filter.Capabilities; 11 | import org.geotools.filter.capability.FilterCapabilitiesImpl; 12 | import org.geotools.filter.capability.TemporalCapabilitiesImpl; 13 | import org.geotools.filter.capability.TemporalOperatorImpl; 14 | import org.geotools.filter.visitor.IsFullySupportedFilterVisitor; 15 | import org.opengis.filter.ExcludeFilter; 16 | import org.opengis.filter.Filter; 17 | import org.opengis.filter.Id; 18 | import org.opengis.filter.IncludeFilter; 19 | import org.opengis.filter.PropertyIsBetween; 20 | import org.opengis.filter.PropertyIsLike; 21 | import org.opengis.filter.PropertyIsNull; 22 | import org.opengis.filter.capability.TemporalCapabilities; 23 | import org.opengis.filter.capability.TemporalOperators; 24 | import org.opengis.filter.spatial.BBOX; 25 | import org.opengis.filter.spatial.Beyond; 26 | import org.opengis.filter.spatial.Contains; 27 | import org.opengis.filter.spatial.DWithin; 28 | import org.opengis.filter.spatial.Disjoint; 29 | import org.opengis.filter.spatial.Intersects; 30 | import org.opengis.filter.spatial.Within; 31 | import org.opengis.filter.temporal.After; 32 | import org.opengis.filter.temporal.AnyInteracts; 33 | import org.opengis.filter.temporal.Before; 34 | import org.opengis.filter.temporal.Begins; 35 | import org.opengis.filter.temporal.BegunBy; 36 | import org.opengis.filter.temporal.BinaryTemporalOperator; 37 | import org.opengis.filter.temporal.During; 38 | import org.opengis.filter.temporal.EndedBy; 39 | import org.opengis.filter.temporal.Ends; 40 | import org.opengis.filter.temporal.Meets; 41 | import org.opengis.filter.temporal.MetBy; 42 | import org.opengis.filter.temporal.OverlappedBy; 43 | import org.opengis.filter.temporal.TContains; 44 | import org.opengis.filter.temporal.TEquals; 45 | import org.opengis.filter.temporal.TOverlaps; 46 | 47 | /** 48 | * Custom {@link Capabilities} supporting temporal capabilities and operators. Uses a custom {@link IsFullySupportedFilterVisitor} 49 | * to enable support for {@link IncludeFilter}, {@link ExcludeFilter} and {@link BegunBy}. 50 | * 51 | */ 52 | class ElasticCapabilities extends Capabilities { 53 | private static final Map,String> temporalNames; 54 | static { 55 | temporalNames = new HashMap<>(); 56 | temporalNames.put(After.class, After.NAME ); 57 | temporalNames.put(AnyInteracts.class, AnyInteracts.NAME ); 58 | temporalNames.put(Before.class, Before.NAME ); 59 | temporalNames.put(Begins.class, Begins.NAME ); 60 | temporalNames.put(BegunBy.class, BegunBy.NAME ); 61 | temporalNames.put(During.class, During.NAME ); 62 | temporalNames.put(EndedBy.class, EndedBy.NAME ); 63 | temporalNames.put(Ends.class, Ends.NAME ); 64 | temporalNames.put(Meets.class, Meets.NAME ); 65 | temporalNames.put(MetBy.class, MetBy.NAME ); 66 | temporalNames.put(OverlappedBy.class, OverlappedBy.NAME ); 67 | temporalNames.put(TContains.class, TContains.NAME ); 68 | temporalNames.put(TEquals.class, TEquals.NAME ); 69 | temporalNames.put(TOverlaps.class, TOverlaps.NAME ); 70 | } 71 | 72 | private IsFullySupportedFilterVisitor fullySupportedVisitor; 73 | 74 | public ElasticCapabilities() { 75 | super(new ElasticFilterCapabilities()); 76 | 77 | addAll(LOGICAL_OPENGIS); 78 | addAll(SIMPLE_COMPARISONS_OPENGIS); 79 | addType(PropertyIsNull.class); 80 | addType(PropertyIsBetween.class); 81 | addType(Id.class); 82 | addType(IncludeFilter.class); 83 | addType(ExcludeFilter.class); 84 | addType(PropertyIsLike.class); 85 | 86 | // spatial filters 87 | addType(BBOX.class); 88 | addType(Contains.class); 89 | //addType(Crosses.class); 90 | addType(Disjoint.class); 91 | //addType(Equals.class); 92 | addType(Intersects.class); 93 | //addType(Overlaps.class); 94 | //addType(Touches.class); 95 | addType(Within.class); 96 | addType(DWithin.class); 97 | addType(Beyond.class); 98 | 99 | //temporal filters 100 | addType(After.class); 101 | addType(Before.class); 102 | addType(Begins.class); 103 | addType(BegunBy.class); 104 | addType(During.class); 105 | addType(Ends.class); 106 | addType(EndedBy.class); 107 | addType(TContains.class); 108 | addType(TEquals.class); 109 | } 110 | 111 | @Override 112 | public boolean fullySupports(Filter filter) { 113 | if( fullySupportedVisitor == null ){ 114 | fullySupportedVisitor = new ElasticIsFullySupportedFilterVisitor(); 115 | } 116 | return filter != null ? (Boolean) filter.accept( fullySupportedVisitor, null ) : false; 117 | } 118 | 119 | @Override 120 | public String toOperationName( @SuppressWarnings("rawtypes") Class filterType ) { 121 | if (filterType != null && temporalNames.containsKey(filterType)) { 122 | return temporalNames.get(filterType); 123 | } 124 | return super.toOperationName(filterType); 125 | } 126 | 127 | @Override 128 | public void addName( String name ) { 129 | if (name != null && temporalNames.containsValue(name)) { 130 | final TemporalOperators operators = getContents().getTemporalCapabilities().getTemporalOperators(); 131 | operators.getOperators().add(new TemporalOperatorImpl(name)); 132 | } else { 133 | super.addName(name); 134 | } 135 | } 136 | 137 | private static class ElasticFilterCapabilities extends FilterCapabilitiesImpl { 138 | 139 | TemporalCapabilitiesImpl temporal; 140 | 141 | @Override 142 | public TemporalCapabilities getTemporalCapabilities() { 143 | if( temporal == null ){ 144 | temporal = new TemporalCapabilitiesImpl(); 145 | super.setTemporal(temporal); 146 | } 147 | return temporal; 148 | } 149 | 150 | } 151 | 152 | private class ElasticIsFullySupportedFilterVisitor extends IsFullySupportedFilterVisitor { 153 | 154 | ElasticIsFullySupportedFilterVisitor() { 155 | super(getContents()); 156 | } 157 | 158 | public Object visit( ExcludeFilter filter, Object extraData ) { 159 | return true; 160 | } 161 | 162 | public Object visit( IncludeFilter filter, Object extraData ) { 163 | return true; 164 | } 165 | 166 | public Object visit(BegunBy begunBy, Object extraData) { 167 | return visit((BinaryTemporalOperator)begunBy, BegunBy.NAME); 168 | } 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.io.Closeable; 8 | import java.io.IOException; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | interface ElasticClient extends Closeable { 14 | 15 | String RUN_AS = "es-security-runas-user"; 16 | 17 | double getVersion(); 18 | 19 | List getTypes(String indexName) throws IOException; 20 | 21 | Map getMapping(String indexName, String type) throws IOException; 22 | 23 | ElasticResponse search(String searchIndices, String type, ElasticRequest request) throws IOException; 24 | 25 | ElasticResponse scroll(String scrollId, Integer scrollTime) throws IOException; 26 | 27 | @Override 28 | void close() throws IOException; 29 | 30 | void clearScroll(Set scrollIds) throws IOException; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.util.Collections; 8 | import java.util.Map; 9 | 10 | import com.google.common.collect.ImmutableMap; 11 | 12 | import mil.nga.giat.data.elasticsearch.ElasticAttribute.ElasticGeometryType; 13 | 14 | final class ElasticConstants { 15 | 16 | public static final Map MATCH_ALL = ImmutableMap.of("match_all", Collections.EMPTY_MAP); 17 | 18 | /** 19 | * Key used in the feature type user data to store the format for date 20 | * fields, if relevant. 21 | */ 22 | public static final String DATE_FORMAT = "date_format"; 23 | 24 | /** 25 | * Key used in the feature type user data to store the full name for fields. 26 | */ 27 | public static final String FULL_NAME = "full_name"; 28 | 29 | /** 30 | * Key used in the feature type user data to store the Elasticsearch geometry 31 | * type ({@link ElasticGeometryType}). 32 | */ 33 | public static final String GEOMETRY_TYPE = "geometry_type"; 34 | 35 | /** 36 | * Key used in the feature type user data to indicate whether the field is analyzed. 37 | */ 38 | public static final String ANALYZED = "analyzed"; 39 | 40 | /** 41 | * Key used in the feature type user data to indicate whether the field is nested. 42 | */ 43 | public static final String NESTED = "nested"; 44 | 45 | } 46 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticFeatureReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | 9 | import mil.nga.giat.data.elasticsearch.ElasticDataStore.ArrayEncoding; 10 | import mil.nga.giat.shaded.es.common.joda.Joda; 11 | import mil.nga.giat.shaded.joda.time.format.DateTimeFormatter; 12 | 13 | import static mil.nga.giat.data.elasticsearch.ElasticConstants.DATE_FORMAT; 14 | import static mil.nga.giat.data.elasticsearch.ElasticConstants.FULL_NAME; 15 | 16 | import org.geotools.data.FeatureReader; 17 | import org.geotools.data.store.ContentState; 18 | import org.geotools.feature.simple.SimpleFeatureBuilder; 19 | import org.geotools.util.logging.Logging; 20 | import org.locationtech.jts.geom.Geometry; 21 | import org.opengis.feature.simple.SimpleFeature; 22 | import org.opengis.feature.simple.SimpleFeatureType; 23 | import org.opengis.feature.type.AttributeDescriptor; 24 | 25 | import java.io.IOException; 26 | import java.util.Collections; 27 | import java.util.Date; 28 | import java.util.Iterator; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.logging.Logger; 32 | 33 | /** 34 | * FeatureReader access to the Elasticsearch index. 35 | */ 36 | class ElasticFeatureReader implements FeatureReader { 37 | 38 | private final static Logger LOGGER = Logging.getLogger(ElasticFeatureReader.class); 39 | 40 | private final ContentState state; 41 | 42 | private final SimpleFeatureType featureType; 43 | 44 | private final float maxScore; 45 | 46 | private final ObjectMapper mapper; 47 | 48 | private final ArrayEncoding arrayEncoding; 49 | 50 | private SimpleFeatureBuilder builder; 51 | 52 | private Iterator searchHitIterator; 53 | 54 | private Iterator> aggregationIterator; 55 | 56 | private final ElasticParserUtil parserUtil; 57 | 58 | public ElasticFeatureReader(ContentState contentState, ElasticResponse response) { 59 | this(contentState, response.getHits(), response.getAggregations(), response.getMaxScore()); 60 | } 61 | 62 | public ElasticFeatureReader(ContentState contentState, List hits, Map aggregations, float maxScore) { 63 | this.state = contentState; 64 | this.featureType = state.getFeatureType(); 65 | this.searchHitIterator = hits.iterator(); 66 | this.builder = new SimpleFeatureBuilder(featureType); 67 | this.parserUtil = new ElasticParserUtil(); 68 | this.maxScore = maxScore; 69 | 70 | this.aggregationIterator = Collections.emptyIterator(); 71 | if (aggregations != null && !aggregations.isEmpty()) { 72 | String aggregationName = aggregations.keySet().stream().findFirst().orElse(null); 73 | if (aggregations.size() > 1) { 74 | LOGGER.info("Result has multiple aggregations. Using " + aggregationName); 75 | } 76 | if (aggregations.get(aggregationName).getBuckets() != null) { 77 | this.aggregationIterator = aggregations.get(aggregationName).getBuckets().iterator(); 78 | } 79 | } 80 | 81 | if (contentState.getEntry() != null && contentState.getEntry().getDataStore() != null) { 82 | final ElasticDataStore dataStore; 83 | dataStore = (ElasticDataStore) contentState.getEntry().getDataStore(); 84 | this.arrayEncoding = dataStore.getArrayEncoding(); 85 | } else { 86 | this.arrayEncoding = ArrayEncoding.valueOf((String) ElasticDataStoreFactory.ARRAY_ENCODING.getDefaultValue()); 87 | } 88 | 89 | this.mapper = new ObjectMapper(); 90 | } 91 | 92 | @Override 93 | public SimpleFeatureType getFeatureType() { 94 | return this.featureType; 95 | } 96 | 97 | @Override 98 | public SimpleFeature next() { 99 | final String id; 100 | if (searchHitIterator.hasNext()) { 101 | id = nextHit(); 102 | } else { 103 | nextAggregation(); 104 | id = null; 105 | } 106 | return builder.buildFeature(id); 107 | } 108 | 109 | private String nextHit() { 110 | final ElasticHit hit = searchHitIterator.next(); 111 | final SimpleFeatureType type = getFeatureType(); 112 | final Map source = hit.getSource(); 113 | 114 | final Float score; 115 | final Float relativeScore; 116 | if (hit.getScore() != null && !Float.isNaN(hit.getScore()) && maxScore>0) { 117 | score = hit.getScore(); 118 | relativeScore = score / maxScore; 119 | } else { 120 | score = null; 121 | relativeScore = null; 122 | } 123 | 124 | for (final AttributeDescriptor descriptor : type.getAttributeDescriptors()) { 125 | final String name = descriptor.getType().getName().getLocalPart(); 126 | final String sourceName = (String) descriptor.getUserData().get(FULL_NAME); 127 | 128 | List values = hit.field(sourceName); 129 | if (values == null && source != null) { 130 | // read field from source 131 | values = parserUtil.readField(source, sourceName); 132 | } 133 | 134 | if (values == null && sourceName.equals("_id")) { 135 | builder.set(name, hit.getId()); 136 | } else if (values == null && sourceName.equals("_index")) { 137 | builder.set(name, hit.getIndex()); 138 | } else if (values == null && sourceName.equals("_type")) { 139 | builder.set(name, hit.getType()); 140 | } else if (values == null && sourceName.equals("_score")) { 141 | builder.set(name, score); 142 | } else if (values == null && sourceName.equals("_relative_score")) { 143 | builder.set(name, relativeScore); 144 | } else if (values != null && Geometry.class.isAssignableFrom(descriptor.getType().getBinding())) { 145 | if (values.size() == 1) { 146 | builder.set(name, parserUtil.createGeometry(values.get(0))); 147 | } else { 148 | builder.set(name, parserUtil.createGeometry(values)); 149 | } 150 | } else if (values != null && Date.class.isAssignableFrom(descriptor.getType().getBinding())) { 151 | Object dataVal = values.get(0); 152 | if (dataVal instanceof Double) { 153 | builder.set(name, new Date(Math.round((Double) dataVal))); 154 | } else if (dataVal instanceof Integer) { 155 | builder.set(name, new Date((Integer) dataVal)); 156 | } else if (dataVal instanceof Long) { 157 | builder.set(name, new Date((long) dataVal)); 158 | } else { 159 | final String format = (String) descriptor.getUserData().get(DATE_FORMAT); 160 | final DateTimeFormatter dateFormatter = Joda.forPattern(format).parser(); 161 | 162 | Date date = dateFormatter.parseDateTime((String) dataVal).toDate(); 163 | builder.set(name, date); 164 | } 165 | } else if (values != null && values.size() == 1) { 166 | builder.set(name, values.get(0)); 167 | } else if (values != null && !name.equals("_aggregation")) { 168 | final Object value; 169 | if (arrayEncoding == ArrayEncoding.CSV) { 170 | // only include first array element when using CSV array encoding 171 | value = values.get(0); 172 | } else { 173 | value = values; 174 | } 175 | builder.set(name, value); 176 | } 177 | } 178 | 179 | return state.getEntry().getTypeName() + "." + hit.getId(); 180 | } 181 | 182 | private void nextAggregation() { 183 | final Map aggregation = aggregationIterator.next(); 184 | try { 185 | final byte[] data = mapper.writeValueAsBytes(aggregation); 186 | builder.set("_aggregation", data); 187 | } catch (IOException e) { 188 | LOGGER.warning("Unable to set aggregation. Try reloading layer."); 189 | } 190 | } 191 | 192 | @Override 193 | public boolean hasNext() { 194 | return searchHitIterator.hasNext() || aggregationIterator.hasNext(); 195 | } 196 | 197 | @Override 198 | public void close() { 199 | builder = null; 200 | searchHitIterator = null; 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticFeatureReaderScroll.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.io.IOException; 8 | import java.util.HashSet; 9 | import java.util.List; 10 | import java.util.NoSuchElementException; 11 | import java.util.Set; 12 | import java.util.logging.Logger; 13 | 14 | import org.geotools.data.FeatureReader; 15 | import org.geotools.data.store.ContentState; 16 | import org.geotools.util.logging.Logging; 17 | import org.opengis.feature.simple.SimpleFeature; 18 | import org.opengis.feature.simple.SimpleFeatureType; 19 | 20 | class ElasticFeatureReaderScroll implements FeatureReader { 21 | 22 | private final static Logger LOGGER = Logging.getLogger(ElasticFeatureReaderScroll.class); 23 | 24 | private final ContentState contentState; 25 | 26 | private final int maxFeatures; 27 | 28 | private String nextScrollId; 29 | 30 | private ElasticFeatureReader delegate; 31 | 32 | private int numFeatures; 33 | 34 | private boolean lastScroll; 35 | 36 | private final Set scrollIds; 37 | 38 | public ElasticFeatureReaderScroll(ContentState contentState, ElasticResponse searchResponse, int maxFeatures) { 39 | this.contentState = contentState; 40 | this.maxFeatures = maxFeatures; 41 | this.numFeatures = 0; 42 | this.scrollIds = new HashSet<>(); 43 | processResponse(searchResponse); 44 | } 45 | 46 | private void advanceScroll() throws IOException { 47 | final ElasticDataStore dataStore; 48 | dataStore = (ElasticDataStore) contentState.getEntry().getDataStore(); 49 | processResponse(dataStore.getClient().scroll(nextScrollId, dataStore.getScrollTime())); 50 | } 51 | 52 | private void processResponse(ElasticResponse searchResponse) { 53 | final int numHits = searchResponse.getNumHits(); 54 | final List hits; 55 | if (numFeatures+numHits <= maxFeatures) { 56 | hits = searchResponse.getResults().getHits(); 57 | } else { 58 | final int n = maxFeatures-numFeatures; 59 | hits = searchResponse.getResults().getHits().subList(0,n); 60 | } 61 | delegate = new ElasticFeatureReader(contentState, hits, searchResponse.getAggregations(), 0); 62 | nextScrollId = searchResponse.getScrollId(); 63 | lastScroll = numHits == 0 || numFeatures+hits.size()>=maxFeatures; 64 | LOGGER.fine("Scoll numHits=" + hits.size() + " (total=" + numFeatures+hits.size()); 65 | scrollIds.add(nextScrollId); 66 | } 67 | 68 | @Override 69 | public SimpleFeatureType getFeatureType() { 70 | return delegate.getFeatureType(); 71 | } 72 | 73 | @Override 74 | public SimpleFeature next() throws IOException { 75 | final SimpleFeature feature; 76 | if (hasNext()) { 77 | numFeatures++; 78 | feature = delegate.next(); 79 | } else { 80 | throw new NoSuchElementException(); 81 | } 82 | return feature; 83 | } 84 | 85 | @Override 86 | public boolean hasNext() throws IOException { 87 | if (!delegate.hasNext() && !lastScroll) { 88 | advanceScroll(); 89 | } 90 | return (delegate.hasNext() || !lastScroll) && numFeatures attributes; 36 | 37 | public ElasticFeatureTypeBuilder(List attributes, Name name) { 38 | setName(name); 39 | this.attributes = attributes; 40 | } 41 | 42 | @Override 43 | public SimpleFeatureType buildFeatureType() { 44 | if (attributes != null) { 45 | String defaultGeometryName = null; 46 | for (ElasticAttribute attribute : attributes) { 47 | if (attribute.isUse()) { 48 | final String attributeName; 49 | if (attribute.getCustomName() != null) { 50 | attributeName = attribute.getCustomName(); 51 | } else if (attribute.getUseShortName()) { 52 | attributeName = attribute.getShortName(); 53 | } else { 54 | attributeName = attribute.getName(); 55 | } 56 | 57 | AttributeDescriptor att = null; 58 | if (Geometry.class.isAssignableFrom(attribute.getType())) { 59 | final Integer srid = attribute.getSrid(); 60 | try { 61 | if (srid != null) { 62 | attributeBuilder.setCRS(CRS.decode("EPSG:" + srid)); 63 | attributeBuilder.setName(attributeName); 64 | attributeBuilder.setBinding(attribute.getType()); 65 | att = attributeBuilder.buildDescriptor(attributeName, 66 | attributeBuilder.buildGeometryType()); 67 | 68 | final ElasticGeometryType geometryType = attribute.getGeometryType(); 69 | att.getUserData().put(GEOMETRY_TYPE, geometryType); 70 | if (attribute.isDefaultGeometry() != null 71 | && attribute.isDefaultGeometry()) { 72 | defaultGeometryName = attributeName; 73 | } 74 | } 75 | } catch (Exception e) { 76 | String msg = "Error occured determing srid for " + attribute.getName(); 77 | LOGGER.log(Level.WARNING, msg, e); 78 | } 79 | } else { 80 | attributeBuilder.setName(attributeName); 81 | attributeBuilder.setBinding(attribute.getType()); 82 | att = attributeBuilder.buildDescriptor(attributeName, 83 | attributeBuilder.buildType()); 84 | } 85 | if (att != null && attribute.getDateFormat() != null) { 86 | att.getUserData().put(DATE_FORMAT, attribute.getDateFormat()); 87 | } 88 | if (att != null) { 89 | att.getUserData().put(FULL_NAME, attribute.getName()); 90 | att.getUserData().put(ANALYZED, attribute.getAnalyzed()); 91 | att.getUserData().put(NESTED, attribute.isNested()); 92 | add(att); 93 | } 94 | } 95 | } 96 | if (defaultGeometryName != null) { 97 | setDefaultGeometry(defaultGeometryName); 98 | } 99 | } 100 | return super.buildFeatureType(); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticHit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 11 | import com.fasterxml.jackson.annotation.JsonProperty; 12 | 13 | @SuppressWarnings("unused") 14 | @JsonIgnoreProperties(ignoreUnknown=true) 15 | class ElasticHit { 16 | 17 | @JsonProperty("_index") 18 | private String index; 19 | 20 | @JsonProperty("_type") 21 | private String type; 22 | 23 | @JsonProperty("_id") 24 | private String id; 25 | 26 | @JsonProperty("_score") 27 | private Float score; 28 | 29 | @JsonProperty("_source") 30 | private Map source; 31 | 32 | @JsonProperty("fields") 33 | private Map> fields; 34 | 35 | public String getIndex() { 36 | return index; 37 | } 38 | 39 | public String getType() { 40 | return type; 41 | } 42 | 43 | public String getId() { 44 | return id; 45 | } 46 | 47 | public Float getScore() { 48 | return score; 49 | } 50 | 51 | public Map getSource() { 52 | return source; 53 | } 54 | 55 | public Map> getFields() { 56 | return fields; 57 | } 58 | 59 | public List field(String name) { 60 | return this.fields != null ? this.fields.get(name) : null; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticLayerConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.io.Serializable; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * Describes an Elasticsearch layer configuration as set of {@link ElasticAttribute} 13 | */ 14 | public class ElasticLayerConfiguration implements Serializable { 15 | 16 | private static final long serialVersionUID = 1838874365349725912L; 17 | 18 | /** 19 | * Key to identify the Elasticsearch layer configuration. 20 | */ 21 | public static final String KEY = "ElasticLayerConfiguration"; 22 | 23 | private final String docType; 24 | 25 | private String layerName; 26 | 27 | private final List attributes; 28 | 29 | public ElasticLayerConfiguration(String docType) { 30 | this.docType = docType; 31 | this.layerName = docType; 32 | this.attributes = new ArrayList<>(); 33 | } 34 | 35 | public ElasticLayerConfiguration(ElasticLayerConfiguration other) { 36 | this(other.docType); 37 | setLayerName(other.layerName); 38 | for (final ElasticAttribute attribute : other.attributes) { 39 | attributes.add(new ElasticAttribute(attribute)); 40 | } 41 | } 42 | 43 | public String getDocType() { 44 | return docType; 45 | } 46 | 47 | public String getLayerName() { 48 | return layerName; 49 | } 50 | 51 | public void setLayerName(String layerName) { 52 | this.layerName = layerName; 53 | } 54 | 55 | public List getAttributes() { 56 | return attributes; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticMappings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.util.Map; 8 | 9 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 10 | 11 | @SuppressWarnings("unused") 12 | class ElasticMappings { 13 | 14 | private Map mappings; 15 | 16 | public Map getMappings() { 17 | return mappings; 18 | } 19 | 20 | public void setMappings(Map mappings) { 21 | this.mappings = mappings; 22 | } 23 | 24 | @JsonIgnoreProperties(ignoreUnknown=true) 25 | public static class Mapping { 26 | 27 | private Map properties; 28 | 29 | public Map getProperties() { 30 | return properties; 31 | } 32 | } 33 | 34 | public static class Untyped { 35 | 36 | private Mapping mappings; 37 | 38 | public Mapping getMappings() { 39 | return mappings; 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | class ElasticRequest { 13 | 14 | private Map query; 15 | 16 | private Map>> aggregations; 17 | 18 | private Integer size; 19 | 20 | private Integer from; 21 | 22 | private Integer scroll; 23 | 24 | private final List> sorts; 25 | 26 | private final List sourceIncludes; 27 | 28 | private final List fields; 29 | 30 | public ElasticRequest() { 31 | this.sorts = new ArrayList<>(); 32 | this.fields = new ArrayList<>(); 33 | this.sourceIncludes = new ArrayList<>(); 34 | } 35 | 36 | public Map getQuery() { 37 | return query; 38 | } 39 | 40 | public void setQuery(Map query) { 41 | this.query = query; 42 | } 43 | 44 | public Map>> getAggregations() { 45 | return aggregations; 46 | } 47 | 48 | public void setAggregations(Map>> aggregations) { 49 | this.aggregations = aggregations; 50 | } 51 | 52 | public Integer getSize() { 53 | return size; 54 | } 55 | 56 | public void setSize(Integer size) { 57 | this.size = size; 58 | } 59 | 60 | public Integer getFrom() { 61 | return from; 62 | } 63 | 64 | public void setFrom(Integer from) { 65 | this.from = from; 66 | } 67 | 68 | public Integer getScroll() { 69 | return scroll; 70 | } 71 | 72 | public void setScroll(Integer scroll) { 73 | this.scroll = scroll; 74 | } 75 | 76 | public List> getSorts() { 77 | return sorts; 78 | } 79 | 80 | public void addSort(String key, String order) { 81 | this.sorts.add(Collections.singletonMap(key, Collections.singletonMap("order", order))); 82 | } 83 | 84 | public List getSourceIncludes() { 85 | return sourceIncludes; 86 | } 87 | 88 | public void addSourceInclude(String sourceInclude) { 89 | this.sourceIncludes.add(sourceInclude); 90 | } 91 | 92 | public List getFields() { 93 | return fields; 94 | } 95 | 96 | public void addField(String field) { 97 | this.fields.add(field); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | import com.fasterxml.jackson.annotation.JsonIgnore; 12 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 13 | import com.fasterxml.jackson.annotation.JsonProperty; 14 | 15 | @SuppressWarnings("unused") 16 | @JsonIgnoreProperties(ignoreUnknown=true) 17 | public class ElasticResponse { 18 | 19 | @JsonProperty("hits") 20 | private ElasticResults results; 21 | 22 | @JsonProperty("aggregations") 23 | private Map aggregations; 24 | 25 | @JsonProperty("_scroll_id") 26 | private String scrollId; 27 | 28 | public ElasticResults getResults() { 29 | return results; 30 | } 31 | 32 | public Map getAggregations() { 33 | return aggregations; 34 | } 35 | 36 | public String getScrollId() { 37 | return scrollId; 38 | } 39 | 40 | @JsonIgnore 41 | public List getHits() { 42 | final List hits; 43 | if (results != null) { 44 | hits = results.getHits(); 45 | } else { 46 | hits = new ArrayList<>(); 47 | } 48 | return hits; 49 | } 50 | 51 | public int getNumHits() { 52 | final int numHits; 53 | if (results != null) { 54 | numHits = results.getHits().size(); 55 | } else { 56 | numHits = 0; 57 | } 58 | return numHits; 59 | } 60 | 61 | public long getTotalNumHits() { 62 | final long total; 63 | if (results != null && results.getTotal() != null) { 64 | total = results.getTotal(); 65 | } else { 66 | total = 0L; 67 | } 68 | return total; 69 | } 70 | 71 | public float getMaxScore() { 72 | final float maxScore; 73 | if (results != null && results.getMaxScore() != null) { 74 | maxScore = results.getMaxScore(); 75 | } else { 76 | maxScore = 0f; 77 | } 78 | return maxScore; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "ElasticResponse[total=" + 84 | getTotalNumHits() + 85 | ", hits=" + getNumHits() + 86 | ", aggregations=" + aggregations + 87 | ", scrollId=" + scrollId + 88 | ", maxScore=" + getMaxScore() + 89 | "]"; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/ElasticResults.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import java.util.List; 9 | 10 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 11 | import com.fasterxml.jackson.annotation.JsonProperty; 12 | 13 | @SuppressWarnings("unused") 14 | @JsonIgnoreProperties(ignoreUnknown=true) 15 | public class ElasticResults { 16 | 17 | @JsonDeserialize(using = TotalDeserializer.class) 18 | private Long total; 19 | 20 | @JsonProperty("max_score") 21 | private Float maxScore; 22 | 23 | private List hits; 24 | 25 | public Long getTotal() { 26 | return total; 27 | } 28 | 29 | public Float getMaxScore() { 30 | return maxScore; 31 | } 32 | 33 | public List getHits() { 34 | return hits; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/FilterToElasticException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | class FilterToElasticException extends RuntimeException { 8 | 9 | private static final long serialVersionUID = 1819999351118120451L; 10 | 11 | public FilterToElasticException(String msg, Throwable exp) { 12 | super(msg, exp); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/GeohashUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.util.Map; 8 | 9 | import org.locationtech.jts.geom.Envelope; 10 | 11 | import com.github.davidmoten.geo.GeoHash; 12 | 13 | class GeohashUtil { 14 | 15 | public static int computePrecision(Envelope envelope, long size, double threshold) { 16 | return computePrecision(envelope, size, threshold, 1); 17 | } 18 | 19 | private static int computePrecision(Envelope envelope, long size, double threshold, int n) { 20 | return computeSize(envelope, n)/size > threshold ? n : computePrecision(envelope, size, threshold, n+1); 21 | } 22 | 23 | private static double computeSize(Envelope envelope, int n) { 24 | final double area = Math.min(360*180, envelope.getArea()); 25 | return area/(GeoHash.widthDegrees(n)*GeoHash.heightDegrees(n)); 26 | } 27 | 28 | public static void updateGridAggregationPrecision(Map>> aggregations, int precision) { 29 | aggregations.values().stream().filter(a -> a.containsKey("geohash_grid")).forEach(a -> { 30 | Map geohashGrid = a.get("geohash_grid"); 31 | if (!geohashGrid.containsKey("precision")) { 32 | geohashGrid.put("precision", precision); 33 | } 34 | }); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/java/mil/nga/giat/data/elasticsearch/TotalDeserializer.java: -------------------------------------------------------------------------------- 1 | package mil.nga.giat.data.elasticsearch; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.DeserializationContext; 6 | import com.fasterxml.jackson.databind.JsonNode; 7 | import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 8 | import com.fasterxml.jackson.databind.exc.MismatchedInputException; 9 | import java.io.IOException; 10 | 11 | public class TotalDeserializer extends StdDeserializer { 12 | 13 | public TotalDeserializer() { 14 | this(null); 15 | } 16 | 17 | public TotalDeserializer(Class vc) { 18 | super(vc); 19 | } 20 | 21 | @Override 22 | public Long deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { 23 | try { 24 | return jsonParser.readValueAs(Long.class); 25 | } catch (MismatchedInputException e) { 26 | JsonNode node = jsonParser.getCodec().readTree(jsonParser); 27 | return node.get("value").longValue(); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/main/resources/META-INF/services/org.geotools.data.DataStoreFactorySpi: -------------------------------------------------------------------------------- 1 | mil.nga.giat.data.elasticsearch.ElasticDataStoreFactory 2 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/ElasticAggregationReaderTest.java: -------------------------------------------------------------------------------- 1 | package mil.nga.giat.data.elasticsearch; 2 | 3 | import org.geotools.data.DataUtilities; 4 | import org.geotools.data.store.ContentState; 5 | import org.geotools.feature.SchemaException; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.opengis.feature.simple.SimpleFeature; 9 | import org.opengis.feature.simple.SimpleFeatureType; 10 | 11 | import com.fasterxml.jackson.core.type.TypeReference; 12 | import com.fasterxml.jackson.databind.ObjectMapper; 13 | import com.google.common.collect.ImmutableList; 14 | import com.google.common.collect.ImmutableMap; 15 | import com.google.common.collect.ImmutableSet; 16 | 17 | import static org.junit.Assert.*; 18 | 19 | import java.io.IOException; 20 | import java.util.ArrayList; 21 | import java.util.LinkedHashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | public class ElasticAggregationReaderTest { 26 | 27 | private ContentState state; 28 | 29 | private List hits; 30 | 31 | private Map aggregations; 32 | 33 | private ElasticFeatureReader reader; 34 | 35 | private SimpleFeature feature; 36 | 37 | private ObjectMapper mapper; 38 | 39 | @Before 40 | public void setup() throws SchemaException { 41 | SimpleFeatureType featureType = DataUtilities.createType("test", "name:String,_aggregation:java.util.HashMap"); 42 | state = new ContentState(null); 43 | state.setFeatureType(featureType); 44 | hits = new ArrayList<>(); 45 | aggregations = new LinkedHashMap<>(); 46 | mapper = new ObjectMapper(); 47 | } 48 | 49 | @Test 50 | public void testNoAggregations() { 51 | assertFalse((new ElasticFeatureReader(state, hits, aggregations, 0)).hasNext()); 52 | } 53 | 54 | @Test 55 | public void testBuckets() throws IOException { 56 | ElasticAggregation aggregation = new ElasticAggregation(); 57 | aggregation.setBuckets(new ArrayList<>()); 58 | aggregations.put("test", aggregation); 59 | assertFalse((new ElasticFeatureReader(state, hits, aggregations, 0)).hasNext()); 60 | 61 | aggregation.setBuckets(ImmutableList.of(ImmutableMap.of("key1","value1"), ImmutableMap.of("key2","value2"))); 62 | reader = new ElasticFeatureReader(state, hits, aggregations, 0); 63 | assertTrue(reader.hasNext()); 64 | feature = reader.next(); 65 | assertNotNull(feature.getAttribute("_aggregation")); 66 | assertEquals(ImmutableSet.of("key1"), byteArrayToMap(feature.getAttribute("_aggregation")).keySet()); 67 | assertTrue(reader.hasNext()); 68 | feature = reader.next(); 69 | assertNotNull(feature.getAttribute("_aggregation")); 70 | assertEquals(ImmutableSet.of("key2"), byteArrayToMap(feature.getAttribute("_aggregation")).keySet()); 71 | assertFalse(reader.hasNext()); 72 | } 73 | 74 | @Test 75 | public void testMultipleAggregations() throws IOException { 76 | ElasticAggregation aggregation = new ElasticAggregation(); 77 | aggregation.setBuckets(ImmutableList.of(ImmutableMap.of("key1","value1"))); 78 | aggregations.put("test", aggregation); 79 | aggregation = new ElasticAggregation(); 80 | aggregation.setBuckets(ImmutableList.of(ImmutableMap.of("key2","value2"))); 81 | aggregations.put("test2", aggregation); 82 | 83 | reader = new ElasticFeatureReader(state, hits, aggregations, 0); 84 | assertTrue(reader.hasNext()); 85 | feature = reader.next(); 86 | assertNotNull(feature.getAttribute("_aggregation")); 87 | assertEquals(ImmutableSet.of("key1"), byteArrayToMap(feature.getAttribute("_aggregation")).keySet()); 88 | assertFalse(reader.hasNext()); 89 | } 90 | 91 | private Map byteArrayToMap(Object bytes) throws IOException { 92 | return mapper.readValue((byte[]) bytes, new TypeReference>() {}); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/ElasticAttributeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | import java.util.Map; 10 | 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | import mil.nga.giat.data.elasticsearch.ElasticAttribute.ElasticGeometryType; 15 | 16 | public class ElasticAttributeTest { 17 | 18 | private ElasticAttribute attr; 19 | 20 | private String name; 21 | 22 | private String shortName; 23 | 24 | private String customName; 25 | 26 | private String normalizedName; 27 | 28 | private boolean useShortName; 29 | 30 | private Class type; 31 | 32 | private ElasticGeometryType geometryType; 33 | 34 | private boolean use; 35 | 36 | private boolean defaultGeometry; 37 | 38 | private int srid; 39 | 40 | private int order; 41 | 42 | private String dateFormat; 43 | 44 | private boolean analyzed; 45 | 46 | private boolean stored; 47 | 48 | private boolean nested; 49 | 50 | @Before 51 | public void setup() { 52 | name = "theName"; 53 | attr = new ElasticAttribute(name); 54 | shortName = "name"; 55 | useShortName = true; 56 | customName = "XML Custom Name"; 57 | normalizedName = "_XML_Custom_Name"; 58 | type = Map.class; 59 | geometryType = ElasticGeometryType.GEO_SHAPE; 60 | use = true; 61 | defaultGeometry = true; 62 | srid = 10; 63 | order = 1; 64 | dateFormat = "yyyy-mm-dd"; 65 | analyzed = true; 66 | stored = true; 67 | nested = true; 68 | } 69 | 70 | @Test 71 | public void testAttributes() { 72 | attr.setShortName(shortName); 73 | attr.setUseShortName(useShortName); 74 | attr.setCustomName(customName); 75 | attr.setType(type); 76 | attr.setGeometryType(geometryType); 77 | attr.setUse(use); 78 | attr.setDefaultGeometry(defaultGeometry); 79 | attr.setSrid(srid); 80 | attr.setOrder(order); 81 | attr.setDateFormat(dateFormat); 82 | attr.setAnalyzed(analyzed); 83 | attr.setStored(stored); 84 | attr.setNested(nested); 85 | assertEquals(attr.getName(), name); 86 | assertEquals(attr.getShortName(), shortName); 87 | assertEquals(attr.getUseShortName(), useShortName); 88 | assertEquals(attr.getCustomName(), normalizedName); 89 | assertEquals(attr.getType(), type); 90 | assertEquals(attr.getGeometryType(), geometryType); 91 | assertEquals(attr.isUse(), use); 92 | assertEquals(attr.isDefaultGeometry(), defaultGeometry); 93 | assertEquals(attr.getSrid(), srid, 1e-10); 94 | assertEquals(attr.getOrder(), Integer.valueOf(order)); 95 | assertEquals(attr.getDateFormat(), dateFormat); 96 | assertEquals(attr.getAnalyzed(), analyzed); 97 | assertEquals(attr.isStored(), stored); 98 | assertEquals(attr.isNested(), nested); 99 | } 100 | 101 | @Test 102 | public void testDisplayName() { 103 | assertEquals(attr.getDisplayName(), name); 104 | attr.setShortName("name"); 105 | attr.setUseShortName(true); 106 | assertEquals("name", attr.getDisplayName()); 107 | } 108 | 109 | @Test 110 | public void testHashCode() { 111 | assertEquals(attr.hashCode(), (new ElasticAttribute("theName")).hashCode()); 112 | assertTrue(attr.hashCode()!=(new ElasticAttribute("name")).hashCode()); 113 | } 114 | 115 | @Test 116 | public void testEquals() { 117 | assertEquals(attr, new ElasticAttribute("theName")); 118 | assertTrue(!attr.equals(new ElasticAttribute("name"))); 119 | } 120 | 121 | @Test 122 | public void testClone() { 123 | assertEquals(attr, new ElasticAttribute(attr)); 124 | } 125 | 126 | @Test 127 | public void testCompare() { 128 | ElasticAttribute other = new ElasticAttribute("other"); 129 | attr.setOrder(1); 130 | other.setOrder(2); 131 | assertEquals(-1, attr.compareTo(other)); 132 | attr.setOrder(3); 133 | other.setOrder(2); 134 | assertEquals(1, attr.compareTo(other)); 135 | attr.setOrder(null); 136 | other.setOrder(1); 137 | assertEquals(1, attr.compareTo(other)); 138 | attr.setOrder(1); 139 | other.setOrder(null); 140 | assertEquals(-1, attr.compareTo(other)); 141 | other = new ElasticAttribute("zAfter"); 142 | attr.setOrder(null); 143 | other.setOrder(null); 144 | assertTrue(attr.compareTo(other) < 0); 145 | other = new ElasticAttribute("before"); 146 | attr.setOrder(1); 147 | other.setOrder(1); 148 | assertTrue(attr.compareTo(other) > 0); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/ElasticDataStoreFinderIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * GeoTools - The Open Source Java GIS Toolkit 3 | * http://geotools.org 4 | * 5 | * (C) 2014, Open Source Geospatial Foundation (OSGeo) 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; 10 | * version 2.1 of the License. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | */ 17 | package mil.nga.giat.data.elasticsearch; 18 | 19 | import java.io.IOException; 20 | import java.io.Serializable; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.logging.Logger; 25 | import java.util.stream.Collectors; 26 | 27 | import com.google.common.collect.ImmutableMap; 28 | import org.apache.http.HttpHost; 29 | import org.elasticsearch.client.Node; 30 | import org.geotools.data.DataStore; 31 | import org.junit.Test; 32 | 33 | import com.google.common.collect.ImmutableList; 34 | 35 | import static org.junit.Assert.*; 36 | 37 | public class ElasticDataStoreFinderIT extends ElasticTestSupport { 38 | 39 | private static final Logger LOGGER = org.geotools.util.logging.Logging 40 | .getLogger(ElasticDataStoreFinderIT.class); 41 | 42 | @Test 43 | public void testFactoryDefaults() throws IOException { 44 | Map params = createConnectionParams(); 45 | ElasticDataStoreFactory factory = new ElasticDataStoreFactory(); 46 | dataStore = (ElasticDataStore) factory.createDataStore(params); 47 | 48 | ElasticDataStoreFactory fac = new ElasticDataStoreFactory(); 49 | assertEquals(fac.getDisplayName(), ElasticDataStoreFactory.DISPLAY_NAME); 50 | assertEquals(fac.getDescription(), ElasticDataStoreFactory.DESCRIPTION); 51 | assertArrayEquals(fac.getParametersInfo(), ElasticDataStoreFactory.PARAMS); 52 | assertNull(fac.getImplementationHints()); 53 | assertNull(fac.createNewDataStore(null)); 54 | } 55 | 56 | @Test 57 | public void testFactory() throws IOException { 58 | ElasticDataStoreFactory factory = new ElasticDataStoreFactory(); 59 | assertTrue(factory.isAvailable()); 60 | 61 | Map map = new HashMap<>(); 62 | map.put(ElasticDataStoreFactory.HOSTNAME.key, "localhost"); 63 | map.put(ElasticDataStoreFactory.HOSTPORT.key, PORT); 64 | map.put(ElasticDataStoreFactory.INDEX_NAME.key, "sample"); 65 | 66 | DataStore store = factory.createDataStore(map); 67 | 68 | assertNotNull(store); 69 | assertTrue(store instanceof ElasticDataStore); 70 | } 71 | 72 | @Test 73 | public void testFactoryWithMissingRequired() throws IOException { 74 | ElasticDataStoreFactory factory = new ElasticDataStoreFactory(); 75 | assertTrue(factory.isAvailable()); 76 | 77 | assertTrue(!factory.canProcess(ImmutableMap.of(ElasticDataStoreFactory.HOSTNAME.key, "localhost", 78 | ElasticDataStoreFactory.HOSTPORT.key, PORT))); 79 | assertTrue(!factory.canProcess(ImmutableMap.of(ElasticDataStoreFactory.HOSTNAME.key, "localhost", 80 | ElasticDataStoreFactory.INDEX_NAME.key, "test"))); 81 | assertTrue(!factory.canProcess(ImmutableMap.of(ElasticDataStoreFactory.HOSTNAME.key, "localhost"))); 82 | assertTrue(!factory.canProcess(ImmutableMap.of(ElasticDataStoreFactory.HOSTPORT.key, PORT, 83 | ElasticDataStoreFactory.INDEX_NAME.key, "test"))); 84 | assertTrue(!factory.canProcess(ImmutableMap.of(ElasticDataStoreFactory.HOSTPORT.key, PORT))); 85 | assertTrue(!factory.canProcess(ImmutableMap.of(ElasticDataStoreFactory.INDEX_NAME.key, "test"))); 86 | } 87 | 88 | @Test 89 | public void testCreateRestClient() throws IOException { 90 | assertEquals(ImmutableList.of(new HttpHost("localhost", PORT, "http")), getHosts("localhost")); 91 | assertEquals(ImmutableList.of(new HttpHost("localhost.localdomain", PORT, "http")), 92 | getHosts("localhost.localdomain")); 93 | 94 | assertEquals(ImmutableList.of(new HttpHost("localhost", 9201, "http")), 95 | getHosts("localhost:9201")); 96 | assertEquals(ImmutableList.of(new HttpHost("localhost.localdomain", 9201, "http")), 97 | getHosts("localhost.localdomain:9201")); 98 | 99 | assertEquals(ImmutableList.of(new HttpHost("localhost", PORT, "http")), 100 | getHosts("http://localhost")); 101 | assertEquals(ImmutableList.of(new HttpHost("localhost", 9200, "http")), 102 | getHosts("http://localhost:9200")); 103 | assertEquals(ImmutableList.of(new HttpHost("localhost", 9201, "http")), 104 | getHosts("http://localhost:9201")); 105 | 106 | assertEquals(ImmutableList.of(new HttpHost("localhost", PORT, "https")), 107 | getHosts("https://localhost")); 108 | assertEquals(ImmutableList.of(new HttpHost("localhost", 9200, "https")), 109 | getHosts("https://localhost:9200")); 110 | assertEquals(ImmutableList.of(new HttpHost("localhost", 9201, "https")), 111 | getHosts("https://localhost:9201")); 112 | 113 | assertEquals(ImmutableList.of( 114 | new HttpHost("somehost.somedomain", PORT, "http"), 115 | new HttpHost("anotherhost.somedomain", PORT, "http")), 116 | getHosts("somehost.somedomain:9200,anotherhost.somedomain:9200")); 117 | assertEquals(ImmutableList.of( 118 | new HttpHost("somehost.somedomain", PORT, "https"), 119 | new HttpHost("anotherhost.somedomain", PORT, "https")), 120 | getHosts("https://somehost.somedomain:9200,https://anotherhost.somedomain:9200")); 121 | assertEquals(ImmutableList.of( 122 | new HttpHost("somehost.somedomain", PORT, "https"), 123 | new HttpHost("anotherhost.somedomain", PORT, "https")), 124 | getHosts("https://somehost.somedomain:9200, https://anotherhost.somedomain:9200")); 125 | assertEquals(ImmutableList.of( 126 | new HttpHost("somehost.somedomain", PORT, "https"), 127 | new HttpHost("anotherhost.somedomain", PORT, "http")), 128 | getHosts("https://somehost.somedomain:9200,anotherhost.somedomain:9200")); 129 | } 130 | 131 | private List getHosts(String hosts) throws IOException { 132 | Map params = createConnectionParams(); 133 | params.put(ElasticDataStoreFactory.HOSTNAME.key, hosts); 134 | ElasticDataStoreFactory factory = new ElasticDataStoreFactory(); 135 | return factory.createRestClient(params).getNodes().stream().map(Node::getHost).collect(Collectors.toList()); 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/ElasticDataStoreIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * GeoTools - The Open Source Java GIS Toolkit 3 | * http://geotools.org 4 | * 5 | * (C) 2014, Open Source Geospatial Foundation (OSGeo) 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; 10 | * version 2.1 of the License. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | */ 17 | package mil.nga.giat.data.elasticsearch; 18 | 19 | import java.io.IOException; 20 | import java.io.Serializable; 21 | import java.util.Arrays; 22 | import java.util.HashMap; 23 | import java.util.HashSet; 24 | import java.util.Map; 25 | import java.util.concurrent.atomic.AtomicInteger; 26 | 27 | import org.apache.http.HttpEntity; 28 | import org.apache.http.HttpHost; 29 | import org.apache.http.StatusLine; 30 | import org.elasticsearch.client.Request; 31 | import org.elasticsearch.client.Response; 32 | import org.elasticsearch.client.RestClient; 33 | import org.geotools.data.DataStore; 34 | import org.geotools.data.store.ContentFeatureSource; 35 | import org.junit.Test; 36 | import org.opengis.feature.simple.SimpleFeatureType; 37 | 38 | import com.google.common.collect.ImmutableMap; 39 | 40 | import static org.junit.Assert.*; 41 | import static org.mockito.Mockito.any; 42 | import static org.mockito.Mockito.mock; 43 | import static org.mockito.Mockito.when; 44 | 45 | public class ElasticDataStoreIT extends ElasticTestSupport { 46 | 47 | @Test 48 | public void testConstructionWithHostAndPortAndIndex() throws IOException { 49 | Map params = createConnectionParams(); 50 | String host = ElasticDataStoreFactory.getValue(ElasticDataStoreFactory.HOSTNAME, params); 51 | Integer port = ElasticDataStoreFactory.getValue(ElasticDataStoreFactory.HOSTPORT, params); 52 | String indexName = ElasticDataStoreFactory.getValue(ElasticDataStoreFactory.INDEX_NAME, params); 53 | 54 | DataStore dataStore = new ElasticDataStore(host, port, indexName); 55 | String[] typeNames = dataStore.getTypeNames(); 56 | assertTrue(typeNames.length > 0); 57 | } 58 | 59 | @Test 60 | public void testConstructionWithClientAndIndex() throws IOException { 61 | Map params = createConnectionParams(); 62 | String host = ElasticDataStoreFactory.getValue(ElasticDataStoreFactory.HOSTNAME, params); 63 | Integer port = ElasticDataStoreFactory.getValue(ElasticDataStoreFactory.HOSTPORT, params); 64 | String indexName = ElasticDataStoreFactory.getValue(ElasticDataStoreFactory.INDEX_NAME, params); 65 | 66 | HttpHost httpHost = new HttpHost(host, port, "http"); 67 | RestClient client = RestClient.builder(httpHost).build(); 68 | 69 | DataStore dataStore = new ElasticDataStore(client, indexName); 70 | String[] typeNames = dataStore.getTypeNames(); 71 | assertTrue(typeNames.length > 0); 72 | } 73 | 74 | @Test 75 | public void testConstructionWithProxyClientAndIndex() throws IOException { 76 | Map params = createConnectionParams(); 77 | String host = ElasticDataStoreFactory.getValue(ElasticDataStoreFactory.HOSTNAME, params); 78 | Integer port = ElasticDataStoreFactory.getValue(ElasticDataStoreFactory.HOSTPORT, params); 79 | String indexName = ElasticDataStoreFactory.getValue(ElasticDataStoreFactory.INDEX_NAME, params); 80 | 81 | HttpHost httpHost = new HttpHost(host, port, "http"); 82 | RestClient client = RestClient.builder(httpHost).build(); 83 | 84 | DataStore dataStore = new ElasticDataStore(client, client, indexName, false); 85 | String[] typeNames = dataStore.getTypeNames(); 86 | assertTrue(typeNames.length > 0); 87 | } 88 | 89 | @Test(expected=IOException.class) 90 | public void testConstructionWithBadClient() throws IOException { 91 | Map params = createConnectionParams(); 92 | String indexName = ElasticDataStoreFactory.getValue(ElasticDataStoreFactory.INDEX_NAME, params); 93 | 94 | RestClient mockClient = mock(RestClient.class); 95 | Response mockResponse = mock(Response.class); 96 | HttpEntity mockEntity = mock(HttpEntity.class); 97 | StatusLine mockStatusLine = mock(StatusLine.class); 98 | when(mockResponse.getEntity()).thenReturn(mockEntity); 99 | when(mockResponse.getStatusLine()).thenReturn(mockStatusLine); 100 | when(mockStatusLine.getStatusCode()).thenReturn(400); 101 | when(mockClient.performRequest(any(Request.class))).thenReturn(mockResponse); 102 | 103 | new ElasticDataStore(mockClient, indexName); 104 | } 105 | 106 | @Test(expected=IOException.class) 107 | public void testConstructionWithBadProxyClient() throws IOException { 108 | Map params = createConnectionParams(); 109 | String indexName = ElasticDataStoreFactory.getValue(ElasticDataStoreFactory.INDEX_NAME, params); 110 | 111 | RestClient mockClient = mock(RestClient.class); 112 | Response mockResponse = mock(Response.class); 113 | HttpEntity mockEntity = mock(HttpEntity.class); 114 | StatusLine mockStatusLine = mock(StatusLine.class); 115 | when(mockResponse.getEntity()).thenReturn(mockEntity); 116 | when(mockResponse.getStatusLine()).thenReturn(mockStatusLine); 117 | when(mockClient.performRequest(any(Request.class))).thenReturn(mockResponse); 118 | 119 | final AtomicInteger count = new AtomicInteger(0); 120 | when(mockStatusLine.getStatusCode()).thenAnswer((invocation) -> 121 | count.getAndIncrement() == 0 ? 200 : 400 122 | ); 123 | new ElasticDataStore(mockClient, mockClient, indexName, false); 124 | } 125 | 126 | @Test 127 | public void testGetNames() throws IOException { 128 | Map params = createConnectionParams(); 129 | 130 | ElasticDataStoreFactory factory = new ElasticDataStoreFactory(); 131 | DataStore dataStore = factory.createDataStore(params); 132 | String[] typeNames = dataStore.getTypeNames(); 133 | assertTrue(typeNames.length > 0); 134 | } 135 | 136 | @Test 137 | public void testGetNamesByAlias() throws IOException { 138 | Map params = createConnectionParams(); 139 | params.put(ElasticDataStoreFactory.INDEX_NAME.key, indexName + "_alias"); 140 | 141 | ElasticDataStoreFactory factory = new ElasticDataStoreFactory(); 142 | DataStore dataStore = factory.createDataStore(params); 143 | String[] typeNames = dataStore.getTypeNames(); 144 | assertTrue(typeNames.length > 0); 145 | } 146 | 147 | @Test 148 | public void testLayerConfigClone() { 149 | ElasticLayerConfiguration layerConfig = new ElasticLayerConfiguration("d"); 150 | layerConfig.setLayerName("ln"); 151 | layerConfig.getAttributes().add(new ElasticAttribute("a1")); 152 | 153 | ElasticLayerConfiguration layerConfig2 = new ElasticLayerConfiguration(layerConfig); 154 | assertEquals(layerConfig.getDocType(), layerConfig2.getDocType()); 155 | assertEquals(layerConfig.getLayerName(), layerConfig2.getLayerName()); 156 | assertEquals(layerConfig.getAttributes(), layerConfig2.getAttributes()); 157 | } 158 | 159 | @Test 160 | public void testSchema() throws IOException { 161 | Map params = createConnectionParams(); 162 | ElasticDataStoreFactory factory = new ElasticDataStoreFactory(); 163 | ElasticDataStore dataStore = (ElasticDataStore) factory.createDataStore(params); 164 | ContentFeatureSource featureSource = dataStore.getFeatureSource(dataStore.getTypeNames()[0]); 165 | SimpleFeatureType schema = featureSource.getSchema(); 166 | assertTrue(schema.getAttributeCount() > 0); 167 | assertNotNull(schema.getDescriptor("speed_is")); 168 | } 169 | 170 | @Test 171 | public void testSchemaWithValidCustomName() throws Exception { 172 | init(); 173 | Map params = createConnectionParams(); 174 | ElasticDataStoreFactory factory = new ElasticDataStoreFactory(); 175 | ElasticDataStore dataStore = (ElasticDataStore) factory.createDataStore(params); 176 | ElasticLayerConfiguration config2 = new ElasticLayerConfiguration(config); 177 | config2.setLayerName("fake"); 178 | dataStore.setLayerConfiguration(config2); 179 | ContentFeatureSource featureSource = dataStore.getFeatureSource("fake"); 180 | SimpleFeatureType schema = featureSource.getSchema(); 181 | assertTrue(schema.getAttributeCount() > 0); 182 | assertNotNull(schema.getDescriptor("speed_is")); 183 | } 184 | 185 | @Test 186 | public void testIsAnalyzed() { 187 | assertFalse(ElasticDataStore.isAnalyzed(new HashMap<>())); 188 | assertFalse(ElasticDataStore.isAnalyzed(ImmutableMap.of("type", "keyword"))); 189 | assertFalse(ElasticDataStore.isAnalyzed(ImmutableMap.of("type", ImmutableMap.of("type", "keyword")))); 190 | assertFalse(ElasticDataStore.isAnalyzed(ImmutableMap.of("type", "not_valid"))); 191 | assertTrue(ElasticDataStore.isAnalyzed(ImmutableMap.of("type", "text"))); 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/ElasticResponseTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | import java.io.IOException; 10 | 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | import com.fasterxml.jackson.databind.ObjectMapper; 15 | import com.google.common.collect.ImmutableList; 16 | import com.google.common.collect.ImmutableMap; 17 | 18 | public class ElasticResponseTest { 19 | 20 | private ObjectMapper mapper; 21 | 22 | @Before 23 | public void setup() { 24 | mapper = new ObjectMapper(); 25 | } 26 | 27 | @Test 28 | public void testDefaults() { 29 | ElasticResponse response = new ElasticResponse(); 30 | assertEquals(0, response.getTotalNumHits()); 31 | assertEquals(0, response.getNumHits()); 32 | assertTrue(response.getHits().isEmpty()); 33 | } 34 | 35 | @Test 36 | public void testTotalHits() throws IOException { 37 | ElasticResponse response = mapper.readValue("{\"hits\":{\"total\":10}}", ElasticResponse.class); 38 | assertEquals(10, response.getTotalNumHits()); 39 | } 40 | 41 | @Test 42 | public void testNullMaxScore() throws IOException { 43 | ElasticResponse response = mapper.readValue("{\"hits\":{}}", ElasticResponse.class); 44 | assertEquals(0, response.getMaxScore(), 1e-9); 45 | } 46 | 47 | @Test 48 | public void testMaxScore() throws IOException { 49 | ElasticResponse response = mapper.readValue("{\"hits\":{\"max_score\":0.8}}", ElasticResponse.class); 50 | assertEquals(0.8, response.getMaxScore(), 1e-6); 51 | } 52 | 53 | @Test 54 | public void testScroll() throws IOException { 55 | ElasticResponse response = mapper.readValue("{\"_scroll_id\":\"12345\"}", ElasticResponse.class); 56 | assertEquals("12345", response.getScrollId()); 57 | } 58 | 59 | @Test 60 | public void getNumHits() throws IOException { 61 | ElasticResponse response = mapper.readValue("{\"hits\":{\"hits\":[{},{},{}]}}", ElasticResponse.class); 62 | assertEquals(3, response.getNumHits()); 63 | } 64 | 65 | @Test 66 | public void testHitId() throws IOException { 67 | ElasticResponse response = mapper.readValue("{\"hits\":{\"hits\":[{\"_id\": \"5\"}]}}", ElasticResponse.class); 68 | assertEquals(1, response.getResults().getHits().size()); 69 | assertEquals("5", response.getResults().getHits().get(0).getId()); 70 | 71 | } 72 | 73 | @Test 74 | public void testHitIndex() throws IOException { 75 | ElasticResponse response = mapper.readValue("{\"hits\":{\"hits\":[{\"_index\": \"test\"}]}}", ElasticResponse.class); 76 | assertEquals(1, response.getResults().getHits().size()); 77 | assertEquals("test", response.getResults().getHits().get(0).getIndex()); 78 | } 79 | 80 | @Test 81 | public void testHitType() throws IOException { 82 | ElasticResponse response = mapper.readValue("{\"hits\":{\"hits\":[{\"_type\": \"test\"}]}}", ElasticResponse.class); 83 | assertEquals(1, response.getResults().getHits().size()); 84 | assertEquals("test", response.getResults().getHits().get(0).getType()); 85 | } 86 | 87 | @Test 88 | public void testHitScore() throws IOException { 89 | ElasticResponse response = mapper.readValue("{\"hits\":{\"hits\":[{\"_score\": 0.4}]}}", ElasticResponse.class); 90 | assertEquals(1, response.getResults().getHits().size()); 91 | assertEquals(0.4, response.getResults().getHits().get(0).getScore(), 1e-6); 92 | } 93 | 94 | @Test 95 | public void testHitFields() throws IOException { 96 | String content = "{\"hits\":{\"hits\":[{\"fields\": {\"tags\":[\"red\"]}}]}}"; 97 | ElasticResponse response = mapper.readValue(content, ElasticResponse.class); 98 | assertEquals(1, response.getResults().getHits().size()); 99 | assertNotNull(response.getResults().getHits().get(0).field("tags")); 100 | assertEquals(ImmutableList.of("red"), response.getResults().getHits().get(0).field("tags")); 101 | 102 | response = mapper.readValue("{\"hits\":{\"hits\":[{}]}}", ElasticResponse.class); 103 | assertNull(response.getResults().getHits().get(0).field("tags")); 104 | } 105 | 106 | @Test 107 | public void testHitSource() throws IOException { 108 | String content = "{\"hits\":{\"hits\":[{\"_source\": {\"tags\":[\"red\"]}}]}}"; 109 | ElasticResponse response = mapper.readValue(content, ElasticResponse.class); 110 | assertEquals(1, response.getResults().getHits().size()); 111 | assertNotNull(response.getResults().getHits().get(0).getSource()); 112 | assertEquals(ImmutableList.of("red"), response.getResults().getHits().get(0).getSource().get("tags")); 113 | } 114 | 115 | @Test 116 | public void testAggregations() throws IOException { 117 | String content = "{\"aggregations\":{\"first\":{\"buckets\": [{\"key\":\"0\",\"doc_count\":10}]}}}"; 118 | ElasticResponse response = mapper.readValue(content, ElasticResponse.class); 119 | assertEquals(1, response.getAggregations().size()); 120 | assertEquals(1, response.getAggregations().size()); 121 | ElasticAggregation aggregations = response.getAggregations().values().stream().findFirst().orElse(null); 122 | assertNotNull(aggregations); 123 | assertEquals(1, aggregations.getBuckets().size()); 124 | assertEquals(ImmutableMap.of("key","0","doc_count",10), aggregations.getBuckets().get(0)); 125 | } 126 | 127 | @Test 128 | public void testMissingAggregation() throws IOException { 129 | ElasticResponse response = mapper.readValue("{}", ElasticResponse.class); 130 | assertNull(response.getAggregations()); 131 | } 132 | 133 | @Test 134 | public void testToString() throws IOException { 135 | String content = "{\"hits\":{\"hits\":[{\"_source\": {\"tags\":[\"red\"]}}]}, " + 136 | "\"aggregations\":{\"first\":{\"buckets\": [{\"key\":\"0\",\"doc_count\":10}]}}}"; 137 | ElasticResponse response = mapper.readValue(content, ElasticResponse.class); 138 | String responseStr = response.toString(); 139 | assertTrue(responseStr.contains("hits=1")); 140 | assertTrue(responseStr.contains("numBuckets=1")); 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/ElasticTemporalFilterIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * GeoTools - The Open Source Java GIS Toolkit 3 | * http://geotools.org 4 | * 5 | * (C) 2014, Open Source Geospatial Foundation (OSGeo) 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; 10 | * version 2.1 of the License. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | */ 17 | 18 | package mil.nga.giat.data.elasticsearch; 19 | 20 | import java.util.Date; 21 | 22 | import org.geotools.data.simple.SimpleFeatureCollection; 23 | import org.geotools.data.simple.SimpleFeatureIterator; 24 | import org.junit.Test; 25 | import static org.junit.Assert.*; 26 | import org.opengis.feature.simple.SimpleFeature; 27 | import org.opengis.filter.Filter; 28 | import org.opengis.filter.FilterFactory; 29 | import org.opengis.temporal.Period; 30 | 31 | public class ElasticTemporalFilterIT extends ElasticTestSupport { 32 | 33 | @Test 34 | public void testLessDateFilterLong() throws Exception { 35 | init(); 36 | Date testDate = new Date(1005912798000L); 37 | FilterFactory ff = dataStore.getFilterFactory(); 38 | 39 | Filter f = ff.lessOrEqual(ff.property("installed_td"), ff.literal(testDate.getTime())); 40 | SimpleFeatureCollection features = featureSource.getFeatures(f); 41 | assertEquals(4, features.size()); 42 | SimpleFeatureIterator it = features.features(); 43 | while (it.hasNext()) { 44 | SimpleFeature next = it.next(); 45 | Date date = (Date) next.getAttribute("installed_td"); 46 | assertTrue(date.before(testDate) || date.equals(testDate)); 47 | } 48 | it.close(); 49 | } 50 | 51 | 52 | @Test 53 | public void testGreaterDateFilterLong() throws Exception { 54 | init(); 55 | Date testDate = new Date(1005912798000L); 56 | FilterFactory ff = dataStore.getFilterFactory(); 57 | 58 | Filter f = ff.greaterOrEqual(ff.property("installed_td"), ff.literal(testDate.getTime())); 59 | SimpleFeatureCollection features = featureSource.getFeatures(f); 60 | assertEquals(7, features.size()); 61 | SimpleFeatureIterator it = features.features(); 62 | while (it.hasNext()) { 63 | SimpleFeature next = it.next(); 64 | Date date = (Date) next.getAttribute("installed_td"); 65 | assertTrue(date.after(testDate) || date.equals(testDate)); 66 | } 67 | it.close(); 68 | } 69 | 70 | @Test 71 | public void testCompareDateFilter() throws Exception { 72 | init(); 73 | Date testDate = DATE_FORMAT.parse("2009-06-28 00:00:00"); 74 | FilterFactory ff = dataStore.getFilterFactory(); 75 | 76 | Filter f = ff.lessOrEqual(ff.property("installed_tdt"), ff.literal(testDate)); 77 | SimpleFeatureCollection features = featureSource.getFeatures(f); 78 | assertEquals(4, features.size()); 79 | SimpleFeatureIterator it = features.features(); 80 | while (it.hasNext()) { 81 | Date date = (Date) it.next().getAttribute("installed_tdt"); 82 | assertTrue(date.before(testDate) || date.equals(testDate)); 83 | } 84 | it.close(); 85 | 86 | f = ff.greaterOrEqual(ff.property("installed_tdt"), ff.literal(testDate)); 87 | features = featureSource.getFeatures(f); 88 | assertEquals(5, features.size()); 89 | it = features.features(); 90 | while (it.hasNext()) { 91 | Date date = (Date) it.next().getAttribute("installed_tdt"); 92 | assertTrue(date.after(testDate) || date.equals(testDate)); 93 | } 94 | it.close(); 95 | } 96 | 97 | @Test 98 | public void testAfterFilter() throws Exception { 99 | init(); 100 | Date testDate = DATE_FORMAT.parse("2009-28-06 00:00:00"); 101 | FilterFactory ff = dataStore.getFilterFactory(); 102 | Filter f = ff.after(ff.property("installed_tdt"), ff.literal(testDate)); 103 | SimpleFeatureCollection features = featureSource.getFeatures(f); 104 | assertEquals(5, features.size()); 105 | } 106 | 107 | @Test 108 | public void testAfterInterval() throws Exception { 109 | init(); 110 | Period period = period("2011-21-05 00:00:00", "2011-15-09 00:00:00"); 111 | FilterFactory ff = dataStore.getFilterFactory(); 112 | Filter f = ff.after(ff.property("installed_tdt"), ff.literal(period)); 113 | SimpleFeatureCollection features = featureSource.getFeatures(f); 114 | assertEquals(4, features.size()); 115 | } 116 | 117 | @Test 118 | public void testBeforeFilter() throws Exception { 119 | init(); 120 | Date testDate = DATE_FORMAT.parse("2009-28-06 00:00:00"); 121 | FilterFactory ff = dataStore.getFilterFactory(); 122 | Filter f = ff.before(ff.property("installed_tdt"), ff.literal(testDate)); 123 | SimpleFeatureCollection features = featureSource.getFeatures(f); 124 | assertEquals(4, features.size()); 125 | } 126 | 127 | @Test 128 | public void testBeforeInterval() throws Exception { 129 | init(); 130 | Period period = period("2000-12-11 00:00:00", "2011-05-21 00:00:00"); 131 | FilterFactory ff = dataStore.getFilterFactory(); 132 | Filter f = ff.before(ff.property("installed_tdt"), ff.literal(period)); 133 | SimpleFeatureCollection features = featureSource.getFeatures(f); 134 | assertEquals(1, features.size()); 135 | } 136 | 137 | @Test 138 | public void testBegins() throws Exception { 139 | init(); 140 | Period period = period("2004-20-06 03:44:56", "2014-22-06 03:44:56"); 141 | FilterFactory ff = dataStore.getFilterFactory(); 142 | Filter f = ff.begins(ff.property("installed_tdt"), ff.literal(period)); 143 | SimpleFeatureCollection features = featureSource.getFeatures(f); 144 | assertEquals(1, features.size()); 145 | } 146 | 147 | @Test 148 | public void testBegunBy() throws Exception { 149 | init(); 150 | Period period = period("2004-20-06 03:44:56", "2014-22-06 03:44:56"); 151 | FilterFactory ff = dataStore.getFilterFactory(); 152 | Filter f = ff.begunBy(ff.literal(period), ff.property("installed_tdt")); 153 | SimpleFeatureCollection features = featureSource.getFeatures(f); 154 | assertEquals(1, features.size()); 155 | } 156 | 157 | @Test 158 | public void testEnds() throws Exception { 159 | init(); 160 | Period period = period("2002-20-06 03:44:56", "2004-20-06 03:44:56"); 161 | FilterFactory ff = dataStore.getFilterFactory(); 162 | Filter f = ff.ends(ff.property("installed_tdt"), ff.literal(period)); 163 | SimpleFeatureCollection features = featureSource.getFeatures(f); 164 | assertEquals(1, features.size()); 165 | } 166 | 167 | @Test 168 | public void testEndedBy() throws Exception { 169 | init(); 170 | Period period = period("2004-11-06 03:44:56", "2004-20-06 03:44:56"); 171 | FilterFactory ff = dataStore.getFilterFactory(); 172 | Filter f = ff.endedBy(ff.literal(period), ff.property("installed_tdt")); 173 | SimpleFeatureCollection features = featureSource.getFeatures(f); 174 | assertEquals(1, features.size()); 175 | } 176 | 177 | @Test 178 | public void testDuring() throws Exception { 179 | init(); 180 | Period period = period("2004-19-06 03:44:56", "2004-20-06 03:44:58"); 181 | FilterFactory ff = dataStore.getFilterFactory(); 182 | Filter f = ff.during(ff.property("installed_tdt"), ff.literal(period)); 183 | SimpleFeatureCollection features = featureSource.getFeatures(f); 184 | assertEquals(1, features.size()); 185 | } 186 | 187 | @Test 188 | public void testTContains() throws Exception { 189 | init(); 190 | Period period = period("2004-19-06 03:44:56", "2004-20-06 03:44:58"); 191 | FilterFactory ff = dataStore.getFilterFactory(); 192 | Filter f = ff.tcontains(ff.literal(period), ff.property("installed_tdt")); 193 | SimpleFeatureCollection features = featureSource.getFeatures(f); 194 | assertEquals(1, features.size()); 195 | } 196 | 197 | @Test 198 | public void testTEquals() throws Exception { 199 | init(); 200 | Date testDate = DATE_FORMAT.parse("2013-01-10 00:13:11"); 201 | FilterFactory ff = dataStore.getFilterFactory(); 202 | Filter f = ff.tequals(ff.property("installed_tdt"), ff.literal(testDate)); 203 | SimpleFeatureCollection features = featureSource.getFeatures(f); 204 | assertEquals(1, features.size()); 205 | } 206 | 207 | } 208 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/ElasticViewParametersFilterIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * GeoTools - The Open Source Java GIS Toolkit 3 | * http://geotools.org 4 | * 5 | * (C) 2014, Open Source Geospatial Foundation (OSGeo) 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Lesser General Public 9 | * License as published by the Free Software Foundation; 10 | * version 2.1 of the License. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Lesser General Public License for more details. 16 | */ 17 | 18 | package mil.nga.giat.data.elasticsearch; 19 | 20 | import java.net.URLEncoder; 21 | import java.util.HashMap; 22 | import java.util.Map; 23 | 24 | import org.geotools.data.Query; 25 | import org.geotools.data.simple.SimpleFeatureIterator; 26 | import org.geotools.data.store.ContentFeatureCollection; 27 | import org.geotools.util.factory.Hints; 28 | import org.junit.Test; 29 | 30 | import static org.junit.Assert.*; 31 | import org.opengis.filter.FilterFactory; 32 | import org.opengis.filter.PropertyIsEqualTo; 33 | 34 | import com.fasterxml.jackson.databind.ObjectMapper; 35 | import com.google.common.collect.ImmutableMap; 36 | 37 | public class ElasticViewParametersFilterIT extends ElasticTestSupport { 38 | 39 | private final ObjectMapper mapper = new ObjectMapper(); 40 | 41 | @Test 42 | public void testNativeTermQuery() throws Exception { 43 | init("not-active"); 44 | Map vparams = new HashMap<>(); 45 | Map query = ImmutableMap.of("term", ImmutableMap.of("security_ss", "WPA")); 46 | vparams.put("q", mapper.writeValueAsString(query)); 47 | Hints hints = new Hints(Hints.VIRTUAL_TABLE_PARAMETERS, vparams); 48 | Query q = new Query(featureSource.getSchema().getTypeName()); 49 | q.setHints(hints); 50 | FilterFactory ff = dataStore.getFilterFactory(); 51 | PropertyIsEqualTo filter = ff.equals(ff.property("speed_is"), ff.literal("300")); 52 | q.setFilter(filter); 53 | ContentFeatureCollection features = featureSource.getFeatures(q); 54 | assertEquals(1, features.size()); 55 | SimpleFeatureIterator fsi = features.features(); 56 | assertTrue(fsi.hasNext()); 57 | assertEquals(fsi.next().getID(), "active.12"); 58 | } 59 | 60 | @Test 61 | public void testEncodedNativeTermQuery() throws Exception { 62 | init("not-active"); 63 | Map vparams = new HashMap<>(); 64 | Map query = ImmutableMap.of("term", ImmutableMap.of("security_ss", "WPA")); 65 | vparams.put("q", URLEncoder.encode(mapper.writeValueAsString(query), "UTF-8")); 66 | Hints hints = new Hints(Hints.VIRTUAL_TABLE_PARAMETERS, vparams); 67 | Query q = new Query(featureSource.getSchema().getTypeName()); 68 | q.setHints(hints); 69 | FilterFactory ff = dataStore.getFilterFactory(); 70 | PropertyIsEqualTo filter = ff.equals(ff.property("speed_is"), ff.literal("300")); 71 | q.setFilter(filter); 72 | ContentFeatureCollection features = featureSource.getFeatures(q); 73 | assertEquals(1, features.size()); 74 | SimpleFeatureIterator fsi = features.features(); 75 | assertTrue(fsi.hasNext()); 76 | assertEquals(fsi.next().getID(), "active.12"); 77 | } 78 | 79 | @Test 80 | public void testNativeBooleanQuery() throws Exception { 81 | init(); 82 | Map vparams = new HashMap<>(); 83 | Map query = ImmutableMap.of("bool", ImmutableMap.of("must", 84 | ImmutableMap.of("term", ImmutableMap.of("security_ss", "WPA")), 85 | "must_not", ImmutableMap.of("term", ImmutableMap.of("modem_b", true)))); 86 | vparams.put("q", mapper.writeValueAsString(query)); 87 | Hints hints = new Hints(Hints.VIRTUAL_TABLE_PARAMETERS, vparams); 88 | Query q = new Query(featureSource.getSchema().getTypeName()); 89 | q.setHints(hints); 90 | FilterFactory ff = dataStore.getFilterFactory(); 91 | PropertyIsEqualTo filter = ff.equals(ff.property("speed_is"), ff.literal("300")); 92 | q.setFilter(filter); 93 | ContentFeatureCollection features = featureSource.getFeatures(q); 94 | assertEquals(2, features.size()); 95 | SimpleFeatureIterator fsi = features.features(); 96 | assertTrue(fsi.hasNext()); 97 | assertEquals(fsi.next().getAttribute("modem_b"), false); 98 | assertTrue(fsi.hasNext()); 99 | assertEquals(fsi.next().getAttribute("modem_b"), false); 100 | } 101 | 102 | @Test 103 | public void testNativeAggregation() throws Exception { 104 | init(); 105 | Map vparams = new HashMap<>(); 106 | Map query = ImmutableMap.of("agg", ImmutableMap.of("geohash_grid", 107 | ImmutableMap.of("field", "geo", "precision", 3))); 108 | vparams.put("a", mapper.writeValueAsString(query)); 109 | Hints hints = new Hints(Hints.VIRTUAL_TABLE_PARAMETERS, vparams); 110 | Query q = new Query(featureSource.getSchema().getTypeName()); 111 | q.setHints(hints); 112 | ContentFeatureCollection features = featureSource.getFeatures(q); 113 | assertFalse(features.isEmpty()); 114 | SimpleFeatureIterator fsi = features.features(); 115 | assertTrue(fsi.hasNext()); 116 | assertNotNull(fsi.next().getAttribute("_aggregation")); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/GeohashUtilTest.java: -------------------------------------------------------------------------------- 1 | package mil.nga.giat.data.elasticsearch; 2 | 3 | import org.geotools.geometry.jts.ReferencedEnvelope; 4 | import static org.geotools.geometry.jts.ReferencedEnvelope.EVERYTHING; 5 | import org.junit.Test; 6 | 7 | import com.google.common.collect.ImmutableMap; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | public class GeohashUtilTest { 15 | 16 | @Test 17 | public void testComputePrecision() { 18 | assertEquals(1, GeohashUtil.computePrecision(new ReferencedEnvelope(-180,180,-90,90,null), 32, 0.9)); 19 | assertEquals(2, GeohashUtil.computePrecision(new ReferencedEnvelope(-180,180,-90,90,null), 1024, 0.9)); 20 | assertEquals(3, GeohashUtil.computePrecision(new ReferencedEnvelope(-180,180,-90,90,null), 32768, 0.9)); 21 | 22 | assertEquals(2, GeohashUtil.computePrecision(new ReferencedEnvelope(-180,180,-90,90,null), 1000, 0.9)); 23 | assertEquals(3, GeohashUtil.computePrecision(new ReferencedEnvelope(-180,180,-90,90,null), 1500, 0.9)); 24 | 25 | assertEquals(1, GeohashUtil.computePrecision(new ReferencedEnvelope(EVERYTHING.getMinX(),EVERYTHING.getMaxX(),EVERYTHING.getMinY(),EVERYTHING.getMaxY(),null), 32, 0.9)); 26 | 27 | assertEquals(1, GeohashUtil.computePrecision(new ReferencedEnvelope(-1,1,-1,1,null), 0, 0.9)); 28 | assertEquals(6, GeohashUtil.computePrecision(new ReferencedEnvelope(-180,180,-90,90,null), (long) 1e9, 0.9)); 29 | assertEquals(6, GeohashUtil.computePrecision(new ReferencedEnvelope(-180,180,-90,90,null), 1, 1e9)); 30 | assertEquals(1, GeohashUtil.computePrecision(new ReferencedEnvelope(-180,180,-90,90,null), 1, -1e9)); 31 | } 32 | 33 | @Test 34 | public void doNotUpdatePrecisionIfAlreadyDefined() { 35 | final Map geohashGridAgg = new HashMap<>(ImmutableMap.of("field", "name", "precision", 3)); 36 | final Map>> aggregations; 37 | aggregations = ImmutableMap.of("first",ImmutableMap.of("geohash_grid",geohashGridAgg)); 38 | final Map expected = ImmutableMap.of("first", 39 | ImmutableMap.of("geohash_grid",ImmutableMap.of("field","name","precision",3))); 40 | GeohashUtil.updateGridAggregationPrecision(aggregations, 2); 41 | assertEquals(expected, aggregations); 42 | } 43 | 44 | @Test 45 | public void updatePrecisionIfNotDefined() { 46 | final Map geohashGridAgg = new HashMap<>(ImmutableMap.of("field", "name")); 47 | final Map>> aggregations; 48 | aggregations = ImmutableMap.of("first",ImmutableMap.of("geohash_grid",geohashGridAgg)); 49 | final Map expected = ImmutableMap.of("first", 50 | ImmutableMap.of("geohash_grid",ImmutableMap.of("field","name","precision",2))); 51 | GeohashUtil.updateGridAggregationPrecision(aggregations, 2); 52 | assertEquals(expected, aggregations); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/java/mil/nga/giat/data/elasticsearch/RandomGeometryBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is hereby placed into the Public Domain. This means anyone is 3 | * free to do whatever they wish with this file. 4 | */ 5 | package mil.nga.giat.data.elasticsearch; 6 | 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Random; 14 | 15 | import org.geotools.geojson.geom.GeometryJSON; 16 | import org.locationtech.jts.geom.Coordinate; 17 | import org.locationtech.jts.geom.Envelope; 18 | import org.locationtech.jts.geom.Geometry; 19 | import org.locationtech.jts.geom.GeometryCollection; 20 | import org.locationtech.jts.geom.GeometryFactory; 21 | import org.locationtech.jts.geom.LineString; 22 | import org.locationtech.jts.geom.MultiLineString; 23 | import org.locationtech.jts.geom.MultiPoint; 24 | import org.locationtech.jts.geom.MultiPolygon; 25 | import org.locationtech.jts.geom.Point; 26 | import org.locationtech.jts.geom.Polygon; 27 | import org.locationtech.jts.io.WKTWriter; 28 | 29 | import com.fasterxml.jackson.core.type.TypeReference; 30 | import com.fasterxml.jackson.databind.ObjectMapper; 31 | 32 | class RandomGeometryBuilder { 33 | 34 | private final GeometryFactory geometryFactory; 35 | 36 | private final Random random; 37 | 38 | private final GeometryJSON geometryJson; 39 | 40 | private final int decimals; 41 | 42 | private final int numPoints; 43 | 44 | private int numGeometries; 45 | 46 | private final WKTWriter wktWriter; 47 | 48 | public RandomGeometryBuilder() { 49 | geometryFactory = new GeometryFactory(); 50 | random = new Random(123456789L); 51 | decimals = 4; 52 | numPoints = 10; 53 | numGeometries = 2; 54 | geometryJson = new GeometryJSON(decimals); 55 | wktWriter = new WKTWriter(); 56 | } 57 | 58 | public Point createRandomPoint() { 59 | return geometryFactory.createPoint(createRandomCoord()); 60 | } 61 | 62 | public LineString createRandomLineString() { 63 | Coordinate[] coords = new Coordinate[numPoints]; 64 | for (int i=0; i 179 || coord1.y > 89) { 123 | coord1 = createRandomCoord(); 124 | } 125 | final Coordinate coord2 = createRandomCoord((int) (coord1.x+0.5), (int) (coord1.y+0.5)); 126 | return new Envelope(coord1, coord2); 127 | } 128 | 129 | private Coordinate createRandomCoord() { 130 | return createRandomCoord(-180, -90); 131 | } 132 | 133 | private Coordinate createRandomCoord(int minx, int miny) { 134 | int dx = 180 -minx; 135 | int dy = 90 -miny; 136 | final int factor = (int) Math.pow(10, decimals); 137 | final double lon = (random.nextInt(dx*factor)+minx*factor)/((double) factor); 138 | final double lat = (random.nextInt(dy*factor)+miny*factor)/((double) factor); 139 | return new Coordinate(lon, lat); 140 | } 141 | 142 | public Map toMap(Geometry geometry) throws IOException { 143 | final String json = geometryJson.toString(geometry); 144 | return new ObjectMapper().readValue(json, new TypeReference>() {}); 145 | } 146 | 147 | public String toWkt(Geometry geometry) { 148 | return wktWriter.write(geometry); 149 | } 150 | 151 | public Map toMap(Envelope envelope) { 152 | final Map properties = new HashMap<>(); 153 | final List> coordinates = new ArrayList<>(); 154 | coordinates.add(Arrays.asList(envelope.getMinX(), envelope.getMaxY())); 155 | coordinates.add(Arrays.asList(envelope.getMaxX(), envelope.getMinY())); 156 | properties.put("type", "envelope"); 157 | properties.put("coordinates", coordinates); 158 | return properties; 159 | } 160 | 161 | public void setNumGeometries(int numGeometries) { 162 | this.numGeometries = numGeometries; 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/resources/README.md: -------------------------------------------------------------------------------- 1 | JSON data files are based on src/test/resources/wifiAccessPoint.xml from the GeoTools SOLR module: 2 | 3 | GeoTools - The Open Source Java GIS Toolkit 4 | http://geotools.org 5 | 6 | (C) 2014, Open Source Geospatial Foundation (OSGeo) 7 | 8 | This library is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU Lesser General Public 10 | License as published by the Free Software Foundation; 11 | version 2.1 of the License. 12 | 13 | This library is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | Lesser General Public License for more details. 17 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/resources/active_mappings.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "geo": { 4 | "type": "geo_point" 5 | }, 6 | "geo2": { 7 | "type": "geo_shape", 8 | "tree": "quadtree" 9 | }, 10 | "geo3": { 11 | "type": "geo_shape", 12 | "tree": "quadtree" 13 | }, 14 | "geo4": { 15 | "properties": { 16 | "coordinates": { 17 | "type": "geo_point" 18 | } 19 | } 20 | }, 21 | "geo5": { 22 | "type": "geo_point" 23 | }, 24 | "id": { 25 | "type": "keyword" 26 | }, 27 | "installed_tdt": { 28 | "type": "date", 29 | "format": "dateOptionalTime" 30 | }, 31 | "installed_td": { 32 | "type": "date" 33 | }, 34 | "modem_b": { 35 | "type": "boolean", 36 | "store": "true" 37 | }, 38 | "security_ss": { 39 | "type": "keyword", 40 | "store": "true" 41 | }, 42 | "speed_is": { 43 | "type": "integer", 44 | "store": "true" 45 | }, 46 | "standard_ss": { 47 | "type": "keyword" 48 | }, 49 | "status_s": { 50 | "type": "keyword" 51 | }, 52 | "vendor_s": { 53 | "type": "keyword" 54 | }, 55 | "object": { 56 | "properties": { 57 | "hejda": { 58 | "type": "double" 59 | } 60 | } 61 | }, 62 | "nested": { 63 | "type": "nested", 64 | "properties": { 65 | "hej": { 66 | "type": "double" 67 | }, 68 | "parent": { 69 | "type": "nested", 70 | "properties": { 71 | "child": { 72 | "type": "keyword" 73 | } 74 | } 75 | } 76 | } 77 | }, 78 | "long": { 79 | "type": "long" 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/resources/active_mappings_legacy.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "geo": { 4 | "type": "geo_point" 5 | }, 6 | "geo2": { 7 | "type": "geo_shape", 8 | "tree": "quadtree" 9 | }, 10 | "geo3": { 11 | "type": "geo_shape", 12 | "tree": "quadtree" 13 | }, 14 | "geo4": { 15 | "properties": { 16 | "coordinates": { 17 | "type": "geo_point" 18 | } 19 | } 20 | }, 21 | "geo5": { 22 | "type": "geo_point" 23 | }, 24 | "id": { 25 | "type": "string", 26 | "index": "not_analyzed" 27 | }, 28 | "installed_tdt": { 29 | "type": "date", 30 | "format": "dateOptionalTime" 31 | }, 32 | "installed_td": { 33 | "type": "date" 34 | }, 35 | "modem_b": { 36 | "type": "boolean", 37 | "store": "true" 38 | }, 39 | "security_ss": { 40 | "type": "string", 41 | "index": "not_analyzed", 42 | "store": "true" 43 | }, 44 | "speed_is": { 45 | "type": "integer", 46 | "store": "true" 47 | }, 48 | "standard_ss": { 49 | "type": "string", 50 | "index": "not_analyzed" 51 | }, 52 | "status_s": { 53 | "type": "string", 54 | "index": "not_analyzed" 55 | }, 56 | "vendor_s": { 57 | "type": "string", 58 | "index": "not_analyzed" 59 | }, 60 | "object": { 61 | "properties": { 62 | "hejda": { 63 | "type": "double" 64 | } 65 | } 66 | }, 67 | "nested": { 68 | "type": "nested", 69 | "properties": { 70 | "hej": { 71 | "type": "double" 72 | }, 73 | "parent": { 74 | "type": "nested", 75 | "properties": { 76 | "child": { 77 | "type": "string", 78 | "index": "not_analyzed" 79 | } 80 | } 81 | } 82 | } 83 | }, 84 | "long": { 85 | "type": "long" 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/resources/active_mappings_ng.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": { 3 | "geo": { 4 | "type": "geo_point" 5 | }, 6 | "geo2": { 7 | "type": "geo_shape", 8 | "tree": "quadtree" 9 | }, 10 | "geo3": { 11 | "type": "geo_shape", 12 | "tree": "quadtree" 13 | }, 14 | "geo4": { 15 | "properties": { 16 | "coordinates": { 17 | "type": "geo_point" 18 | } 19 | } 20 | }, 21 | "geo5": { 22 | "type": "geo_point" 23 | }, 24 | "geo6": { 25 | "type": "geo_shape", 26 | "tree": "quadtree" 27 | }, 28 | "id": { 29 | "type": "keyword" 30 | }, 31 | "installed_tdt": { 32 | "type": "date", 33 | "format": "dateOptionalTime" 34 | }, 35 | "installed_td": { 36 | "type": "date" 37 | }, 38 | "modem_b": { 39 | "type": "boolean", 40 | "store": "true" 41 | }, 42 | "security_ss": { 43 | "type": "keyword", 44 | "store": "true" 45 | }, 46 | "speed_is": { 47 | "type": "integer", 48 | "store": "true" 49 | }, 50 | "standard_ss": { 51 | "type": "keyword" 52 | }, 53 | "status_s": { 54 | "type": "keyword" 55 | }, 56 | "vendor_s": { 57 | "type": "keyword" 58 | }, 59 | "object": { 60 | "properties": { 61 | "hejda": { 62 | "type": "double" 63 | } 64 | } 65 | }, 66 | "nested": { 67 | "type": "nested", 68 | "properties": { 69 | "hej": { 70 | "type": "double" 71 | }, 72 | "parent": { 73 | "type": "nested", 74 | "properties": { 75 | "child": { 76 | "type": "keyword" 77 | } 78 | } 79 | } 80 | } 81 | }, 82 | "long": { 83 | "type": "long" 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=OFF,A1 2 | 3 | log4j.appender.A1=org.apache.log4j.ConsoleAppender 4 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n 6 | 7 | #log4j.logger.org.springframework=WARN,A1 8 | #log4j.additivity.org.springframework=false 9 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/resources/logging.properties: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # Global properties 3 | ############################################################ 4 | 5 | # "handlers" specifies a comma separated list of log Handler 6 | # classes. These handlers will be installed during VM startup. 7 | # Note that these classes must be on the system classpath. 8 | # By default we only configure a ConsoleHandler, which will only 9 | # show messages at the INFO and above levels. 10 | handlers= java.util.logging.ConsoleHandler 11 | 12 | # Default global logging level. 13 | # This specifies which kinds of events are logged across 14 | # all loggers. For any given facility this global level 15 | # can be overriden by a facility specific level 16 | # Note that the ConsoleHandler also has a separate level 17 | # setting to limit messages printed to the console. 18 | .level= SEVERE 19 | 20 | ############################################################ 21 | # Handler specific properties. 22 | # Describes specific configuration info for Handlers. 23 | ############################################################ 24 | 25 | # Limit the message that are printed on the console to INFO and above. 26 | java.util.logging.ConsoleHandler.level = SEVERE 27 | java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter 28 | 29 | # Example to customize the SimpleFormatter output format 30 | # to print one-line log message like this: 31 | # : [] 32 | # 33 | # java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n 34 | 35 | ############################################################ 36 | # Facility specific properties. 37 | # Provides extra control for each logger. 38 | ############################################################ 39 | 40 | mil.nga.giat.data.elasticsearch.level = FINEST 41 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/resources/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | 3 | -------------------------------------------------------------------------------- /gt-elasticsearch/src/test/resources/test_index.py: -------------------------------------------------------------------------------- 1 | """ 2 | Script creates status_s index for testing. 3 | """ 4 | import argparse 5 | import json 6 | import os 7 | 8 | import requests 9 | 10 | script_dir = os.path.dirname(os.path.realpath(__file__)) 11 | 12 | parser = argparse.ArgumentParser() 13 | parser.add_argument('--attributes', nargs='+', default=[]) 14 | args = parser.parse_args() 15 | 16 | index_name = 'status_s' 17 | f_mappings = 'active_mappings.json' 18 | f_docs = 'wifiAccessPoint.json' 19 | elastic_url = 'http://localhost:9200' 20 | auth = ('elastic', 'changeme') 21 | 22 | version = float(json.loads(requests.get('http://localhost:9200', auth=auth).text)['version']['number'].split('.')[0]) 23 | type_name = 'active' if version < 7 else '_doc' 24 | 25 | requests.delete(f'{elastic_url}/{index_name}', auth=auth) 26 | requests.put(f'{elastic_url}/{index_name}', auth=auth) 27 | 28 | with open(f'{script_dir}/{f_mappings}') as f: 29 | mappings = json.loads(f.read()) 30 | 31 | if args.attributes: 32 | [mappings['properties'].pop(key) for key in list(mappings['properties'].keys()) if key not in args.attributes] 33 | 34 | requests.put(f'{elastic_url}/{index_name}/_mapping/{type_name if version < 7 else ""}', json=mappings, auth=auth) 35 | 36 | with open(f'{script_dir}/{f_docs}') as f: 37 | features = json.load(f)['features'] 38 | 39 | for item in [item for item in features if item['status_s'] == 'active']: 40 | if args.attributes: 41 | [item.pop(key) for key in list(item.keys()) if key not in args.attributes] 42 | 43 | requests.put(f'{elastic_url}/{index_name}/{type_name}/{item["id"]}', json=item, auth=auth) 44 | 45 | requests.put(f'{elastic_url}//_xpack/security/role/status_admin', json={'indices': [{'privileges': ['all'], 'names': ['status*']}]}, auth=auth) 46 | requests.put(f'{elastic_url}/_xpack/security/user/admin', json={'password': 'statusadmin', 'roles': ['status_admin']}, auth=auth) 47 | -------------------------------------------------------------------------------- /joda-shaded/NOTICE.txt: -------------------------------------------------------------------------------- 1 | Elasticsearch 2 | Copyright 2009-2017 Elasticsearch 3 | 4 | This product includes software developed by The Apache Software Foundation 5 | (http://www.apache.org/). 6 | -------------------------------------------------------------------------------- /joda-shaded/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | elasticgeo 6 | mil.nga.giat 7 | 2.16-SNAPSHOT 8 | 9 | joda-shaded 10 | 2.16-SNAPSHOT 11 | jar 12 | Elasticsearch Joda Shaded 13 | 14 | 15 | joda-time 16 | joda-time 17 | ${joda.version} 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-dependency-plugin 25 | 2.10 26 | 27 | 28 | unpack 29 | process-resources 30 | 31 | unpack 32 | 33 | 34 | true 35 | 36 | 37 | org.elasticsearch 38 | elasticsearch 39 | 41 | 6.4.3 42 | **/Joda.java,**/FormatDateTimeFormatter.java,**/StrictISODateTimeFormat.java 43 | ${project.build.directory}/generated-sources 44 | sources 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.codehaus.mojo 53 | build-helper-maven-plugin 54 | 3.0.0 55 | 56 | 57 | generate-sources 58 | 59 | add-source 60 | 61 | 62 | 63 | ${project.build.directory}/generated-sources 64 | 65 | 66 | 67 | 68 | 69 | 70 | maven-shade-plugin 71 | 2.3 72 | 73 | 74 | shade-elasticsearch 75 | package 76 | 77 | shade 78 | 79 | 80 | false 81 | 82 | 83 | 84 | org/joda/** 85 | org/elasticsearch/** 86 | 87 | 88 | 89 | 90 | 91 | org.joda 92 | mil.nga.giat.shaded.joda 93 | 94 | 95 | org.elasticsearch 96 | mil.nga.giat.shaded.es 97 | 98 | 99 | false 100 | false 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /joda-shaded/src/main/java/org/elasticsearch/common/Strings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to Elasticsearch under one or more contributor 3 | * license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright 5 | * ownership. Elasticsearch licenses this file to you under 6 | * the Apache License, Version 2.0 (the "License"); you may 7 | * not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | * 19 | * 2017-05 - Reduced to minimal implementation necessary for 20 | * compiling org.elasticsearch.common.joda.Joda. 21 | */ 22 | 23 | package org.elasticsearch.common; 24 | 25 | public class Strings { 26 | 27 | public static boolean hasLength(String input) { 28 | return input != null && !input.isEmpty(); 29 | } 30 | 31 | public static String[] delimitedListToStringArray(String input, String delimiter) { 32 | return input.split(delimiter); 33 | } 34 | 35 | } 36 | --------------------------------------------------------------------------------