diff --git a/.classpath b/.classpath
index 107e885..8cd988e 100644
--- a/.classpath
+++ b/.classpath
@@ -6,17 +6,22 @@
+
+
+
+
+
+
-
-
+
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
index cac0df4..8b5c4dc 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -1,7 +1,12 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fae0c13..7572d47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [v1-2-0-SNAPSHOT] - 2022-02-01
+
+- [#22747] Added WFS utilities
+
## [v1-1-2] - 2019-10-24
- [#17831#change-92733] Bug fixed
diff --git a/pom.xml b/pom.xml
index 280ae3f..a0f165c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
org.gcube.spatial.data
geo-utility
jar
- 1.1.2-SNAPSHOT
+ 1.2.0-SNAPSHOT
geo-utility
A library with utility classes to interact with geo-spatial data: WmsUrlValidator, GeoGetStyles, etc..
@@ -28,6 +28,18 @@
UTF-8
+
+
+ Francesco Mangiacrapa
+ francesco.mangiacrapa@isti.cnr.it
+ CNR Pisa, Istituto di Scienza e Tecnologie dell'Informazione "A. Faedo"
+
+ architect
+ developer
+
+
+
+
@@ -69,11 +81,12 @@
slf4j-log4j12
1.6.4
-
- log4j
- log4j
- 1.2.16
-
+
+
+
+
+
+
junit
diff --git a/src/main/java/org/gcube/spatial/data/GeoUtility.gwt.xml b/src/main/java/org/gcube/spatial/data/GeoUtility.gwt.xml
new file mode 100644
index 0000000..f41ea40
--- /dev/null
+++ b/src/main/java/org/gcube/spatial/data/GeoUtility.gwt.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/FeatureGeometry.java b/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/FeatureGeometry.java
new file mode 100644
index 0000000..74706c5
--- /dev/null
+++ b/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/FeatureGeometry.java
@@ -0,0 +1,74 @@
+package org.gcube.spatial.data.geoutility.shared.wfs;
+
+import java.io.Serializable;
+
+/**
+ * The Class FeatureGeometry.
+ *
+ * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
+ *
+ * Sep 6, 2021
+ */
+public class FeatureGeometry implements WFSGeometry, Serializable {
+
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 8231377594468260590L;
+ private String type;
+ private String coordinatesJSON;
+ private String toJSON;
+
+ /**
+ * Instantiates a new feature geometry.
+ */
+ public FeatureGeometry() {
+ }
+
+ /**
+ * Instantiates a new feature geometry.
+ *
+ * @param type the type
+ * @param coordinatesJSON the coordinates JSON
+ */
+ public FeatureGeometry(String type, String coordinatesJSON) {
+ super();
+ this.type = type;
+ this.coordinatesJSON = coordinatesJSON;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getCoordinatesJSON() {
+ return coordinatesJSON;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public void setCoordinatesJSON(String coordinatesJSON) {
+ this.coordinatesJSON = coordinatesJSON;
+ }
+
+ public String getToJSONObject() {
+ if(toJSON==null)
+ toJSON = "{\"type\":\""+type+"\",\"coordinates\":"+coordinatesJSON+"}";
+ return toJSON;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("FeatureGeometry [type=");
+ builder.append(type);
+ builder.append(", coordinatesJSON=");
+ builder.append(coordinatesJSON);
+ builder.append("]");
+ return builder.toString();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/FeatureRow.java b/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/FeatureRow.java
new file mode 100644
index 0000000..9c130bc
--- /dev/null
+++ b/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/FeatureRow.java
@@ -0,0 +1,89 @@
+/**
+ *
+ */
+package org.gcube.spatial.data.geoutility.shared.wfs;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The Class FeatureRow.
+ *
+ * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it)
+ *
+ * Oct 29, 2020
+ */
+public class FeatureRow implements Serializable {
+
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 8741559784800931747L;
+
+ private String id;
+
+ private Map> mapProperties;
+
+ private FeatureGeometry geometry;
+
+ private String crsName;
+
+ public FeatureRow() {
+ }
+
+ public FeatureRow(Map> mapProperties, FeatureGeometry geometry) {
+ super();
+ this.mapProperties = mapProperties;
+ this.setGeometry(geometry);
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Map> getMapProperties() {
+ return mapProperties;
+ }
+
+ public void setMapProperties(Map> mapProperties) {
+ this.mapProperties = mapProperties;
+ }
+
+ public String getCrsName() {
+ return crsName;
+ }
+
+ public void setCrsName(String crsName) {
+ this.crsName = crsName;
+ }
+
+ public FeatureGeometry getGeometry() {
+ return geometry;
+ }
+
+ public void setGeometry(FeatureGeometry geometry) {
+ this.geometry = geometry;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("FeatureRow [id=");
+ builder.append(id);
+ builder.append(", mapProperties=");
+ builder.append(mapProperties);
+ builder.append(", geometry=");
+ builder.append(geometry);
+ builder.append(", crsName=");
+ builder.append(crsName);
+ builder.append("]");
+ return builder.toString();
+ }
+
+}
diff --git a/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/WFSGeometry.java b/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/WFSGeometry.java
new file mode 100644
index 0000000..ee9c387
--- /dev/null
+++ b/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/WFSGeometry.java
@@ -0,0 +1,27 @@
+package org.gcube.spatial.data.geoutility.shared.wfs;
+
+
+/**
+ * The Interface WFSGeometry.
+ *
+ * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
+ *
+ * Sep 6, 2021
+ */
+public interface WFSGeometry {
+
+ /**
+ * Gets the type.
+ *
+ * @return the type
+ */
+ String getType();
+
+ /**
+ * Gets the coordinates JSON.
+ *
+ * @return the coordinates JSON
+ */
+ String getCoordinatesJSON();
+
+}
diff --git a/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/WFSParameter.java b/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/WFSParameter.java
new file mode 100644
index 0000000..7609f02
--- /dev/null
+++ b/src/main/java/org/gcube/spatial/data/geoutility/shared/wfs/WFSParameter.java
@@ -0,0 +1,60 @@
+package org.gcube.spatial.data.geoutility.shared.wfs;
+
+
+/**
+ * The Enum WFSParameter with default value (parameter,value)
+ *
+ * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
+ *
+ * Jan 28, 2022
+ */
+public enum WFSParameter {
+
+ SERVICE("SERVICE", "WFS"),
+ VERSION("VERSION", "1.1.0"),
+ REQUEST("REQUEST", "GetFeature"),
+ TYPENAME("TYPENAME", ""),
+ STYLES("STYLES", ""),
+ BBOX("BBOX", "-180,-90,180,90"),
+ WIDTH("WIDTH", "676"),
+ HEIGHT("HEIGHT", "230"),
+ SRSNAME("srsName", "EPSG:4326"),
+ CRS("CRS","EPSG:4326"), //WMS 1.3.0 COMPLIANT
+ OUTPUTFORMAT("OUTPUTFORMAT", "application/json"),
+ MAXFEATURES("MAXFEATURES", "10"),
+ PROPERTYNAME("PROPERTYNAME",""),
+ CQL_FILTER("CQL_FILTER","");
+
+ private String parameter;
+ private String value;
+
+ /**
+ * Instantiates a new wfs parameters.
+ *
+ * @param parameter the parameter
+ * @param value the value
+ */
+ WFSParameter(String parameter, String value) {
+ this.parameter = parameter;
+ this.value = value;
+ }
+
+ /**
+ * Gets the parameter.
+ *
+ * @return the parameter
+ */
+ public String getParameter() {
+ return parameter;
+ }
+
+ /**
+ * Gets the value.
+ *
+ * @return the value
+ */
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/src/main/java/org/gcube/spatial/data/geoutility/wfs/FeatureParser.java b/src/main/java/org/gcube/spatial/data/geoutility/wfs/FeatureParser.java
new file mode 100644
index 0000000..fcceaf8
--- /dev/null
+++ b/src/main/java/org/gcube/spatial/data/geoutility/wfs/FeatureParser.java
@@ -0,0 +1,151 @@
+/**
+ *
+ */
+package org.gcube.spatial.data.geoutility.wfs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.io.IOUtils;
+import org.gcube.spatial.data.geoutility.shared.wfs.FeatureGeometry;
+import org.gcube.spatial.data.geoutility.shared.wfs.FeatureRow;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Class FeatureParser.
+ *
+ * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
+ *
+ * Jan 28, 2022
+ */
+public class FeatureParser {
+
+ private static Logger LOG = LoggerFactory.getLogger(FeatureParser.class);
+
+ /**
+ * Gets the WFS features.
+ *
+ * @param wfsEndPoint the wfs end point
+ * @param wfsQB the wfs QB
+ * @return the WFS features
+ */
+ public static List getWFSFeatures(String wfsEndPoint, WFSQueryBuilder wfsQB) {
+
+ return getWFSFeatureProperties(wfsEndPoint + "?" + wfsQB.getQuery());
+ }
+
+ /**
+ * Gets the WFS feature properties.
+ *
+ * @param wfsURLRequest the wfs URL request
+ * @return the WFS feature properties
+ */
+ @SuppressWarnings("unchecked")
+ private static List getWFSFeatureProperties(String wfsURLRequest) {
+ LOG.info("getWFSFeatureProperties for url: " + wfsURLRequest);
+ InputStream is = null;
+ List listFeaturesRow = new ArrayList();
+ try {
+
+ LOG.info("Built WFS URL: " + wfsURLRequest);
+ is = new URL(wfsURLRequest).openStream();
+ String jsonTxt = IOUtils.toString(is);
+
+ if (jsonTxt == null || jsonTxt.isEmpty()) {
+ jsonTxt = "{\"type\":\"FeatureCollection\",\"features\":[]}";
+ }
+
+ // get json object
+ JSONObject json = new JSONObject(jsonTxt);
+ // iterate features
+ JSONArray features = json.getJSONArray("features");
+ if (features.length() == 0) {
+ LOG.info("No features detected in the response, returning empty list");
+ return listFeaturesRow;
+ }
+
+ String featureCRSName = "";
+ try {
+ JSONObject crs = json.getJSONObject("crs");
+ JSONObject crsProp = crs.getJSONObject("properties");
+ featureCRSName = crsProp.getString("name");
+ LOG.info("Crs name found: " + featureCRSName);
+ } catch (Exception e) {
+ LOG.warn("Unable to read the field 'crs'");
+ }
+
+ LOG.info("Features are: " + features.length());
+
+ for (int i = 0; i < features.length(); i++) {
+ final FeatureRow row = new FeatureRow();
+ row.setCrsName(featureCRSName);
+ JSONObject theFeature = ((JSONObject) features.get(i));
+ LOG.debug("Building at index: " + i);
+
+ try {
+ String fetaureId = theFeature.getString("id");
+ row.setId(fetaureId);
+ JSONObject geometry = theFeature.getJSONObject("geometry");
+ String typeValue = geometry.getString("type");
+ FeatureGeometry fg = new FeatureGeometry();
+ fg.setType(typeValue);
+
+ try {
+ JSONArray coordinates = geometry.getJSONArray("coordinates");
+ String coordinateJSONString = coordinates.toString();
+ LOG.debug("coordinates are: " + coordinateJSONString);
+ fg.setCoordinatesJSON(coordinates.toString());
+ } catch (Exception e) {
+ LOG.warn("Not able to parse the 'coordinates' field: ", e);
+ }
+
+ row.setGeometry(fg);
+ } catch (Exception e) {
+ LOG.debug("Unable to parse geometry at index: " + i);
+ }
+
+// // iterate properties
+ JSONObject properties = theFeature.getJSONObject("properties");
+ Map> mapProperties = new HashMap>();
+ @SuppressWarnings("unchecked")
+ Iterator ii = properties.keys();
+ while (ii.hasNext()) {
+ String key = ii.next();
+ String value = properties.optString(key, "");
+
+ List theValues = mapProperties.get(key);
+ if (theValues == null)
+ mapProperties.put(key, Arrays.asList(value));
+ else {
+ theValues.add(value);
+ mapProperties.put(key, theValues);
+ }
+ }
+ row.setMapProperties(mapProperties);
+ listFeaturesRow.add(row);
+ LOG.info("Added row " + row + " to exported properties");
+ }
+
+ } catch (IOException e) {
+ LOG.error("Error on requesting properties for url: " + wfsURLRequest, e);
+ } catch (JSONException e) {
+ LOG.error("Error on requesting properties for url: " + wfsURLRequest, e);
+ } finally {
+ IOUtils.closeQuietly(is);
+ }
+
+ LOG.info("Returning " + listFeaturesRow.size() + " features");
+ return listFeaturesRow;
+ }
+}
diff --git a/src/main/java/org/gcube/spatial/data/geoutility/wfs/WFSQueryBuilder.java b/src/main/java/org/gcube/spatial/data/geoutility/wfs/WFSQueryBuilder.java
new file mode 100644
index 0000000..f3b2ce6
--- /dev/null
+++ b/src/main/java/org/gcube/spatial/data/geoutility/wfs/WFSQueryBuilder.java
@@ -0,0 +1,70 @@
+package org.gcube.spatial.data.geoutility.wfs;
+
+import java.util.List;
+
+import org.gcube.spatial.data.geoutility.shared.wfs.WFSParameter;
+
+/**
+ * The Class WFSQueryBuilder.
+ *
+ * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
+ *
+ * Jan 28, 2022
+ */
+public class WFSQueryBuilder {
+
+ private String query = "";
+
+ /**
+ * Instantiates a new WFS query builder.
+ */
+ public WFSQueryBuilder() {
+
+ }
+
+ /**
+ * Adds the parameter.
+ *
+ * @param param the param
+ * @param value the value
+ */
+ public void addParameter(WFSParameter param, String value) {
+
+ if (!query.isEmpty())
+ query += "&";
+
+ query += param + "=" + value;
+ }
+
+ /**
+ * Adds the parameter.
+ *
+ * @param param the param
+ * @param values the values
+ * @param separtor the separtor
+ */
+ public void addParameter(WFSParameter param, List values, String separtor) {
+
+ if (!query.isEmpty())
+ query += "&";
+
+ String value = "";
+ for (int i = 0; i < values.size() - 1; i++) {
+ value += values.get(i) + separtor;
+ }
+
+ value += values.get(values.size() - 1);
+
+ query += param + "=" + value;
+ }
+
+ /**
+ * Gets the query.
+ *
+ * @return the query
+ */
+ public String getQuery() {
+ return query;
+ }
+
+}
diff --git a/src/main/resources/org/gcube/spatial/data/GeoUtility.gwt.xml b/src/main/resources/org/gcube/spatial/data/GeoUtility.gwt.xml
new file mode 100644
index 0000000..f41ea40
--- /dev/null
+++ b/src/main/resources/org/gcube/spatial/data/GeoUtility.gwt.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/java/org/gcube/spatial/data/geoutility/CustomerTable.java b/src/test/java/org/gcube/spatial/data/geoutility/CustomerTable.java
new file mode 100644
index 0000000..2043a68
--- /dev/null
+++ b/src/test/java/org/gcube/spatial/data/geoutility/CustomerTable.java
@@ -0,0 +1,165 @@
+//package org.gcube.spatial.data.geoutility;
+//
+//import java.util.EnumSet;
+//import java.util.Map;
+//import java.util.stream.Stream;
+//
+//import org.gcube.spatial.data.geoutility.CustomerTable.Column;
+//
+//import com.google.common.base.Predicate;
+//import com.google.common.collect.HashBasedTable;
+//import com.google.common.collect.Maps;
+//import com.google.common.collect.Table;
+//
+//public class CustomerTable {
+//
+// public enum Column {
+// PARENT_LOCATION_ID, LOCATION_ID, LOCATION_TYPE, LOCATION_DEN;
+// }
+//
+// private Table table = HashBasedTable.create();
+//
+// @Override
+// public String toString() {
+// return table.toString();
+// }
+//
+// public void createRow(String[] values) {
+// if (Column.values().length != values.length) {
+// throw new IllegalArgumentException();
+// }
+// Integer rowNum = table.rowKeySet().size() + 1;
+// for (int i = 0; i < values.length; i++) {
+// table.put(rowNum, Column.values()[i], values[i]);
+// }
+// }
+//
+//// private void query() {
+//// // TODO Auto-generated method stub
+////
+//// }
+////
+//// public Table query(Predicate