Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.opengis.cite.ogcapifeatures10.util.JsonUtils Maven / Gradle / Ivy
package org.opengis.cite.ogcapifeatures10.util;
import static io.restassured.RestAssured.given;
import static io.restassured.http.Method.GET;
import static org.opengis.cite.ogcapifeatures10.OgcApiFeatures10.DEFAULT_CRS;
import static org.opengis.cite.ogcapifeatures10.OgcApiFeatures10.GEOJSON_MIME_TYPE;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.LocalDate;
import java.time.Period;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.json.simple.JSONObject;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.geojson.GeoJsonReader;
import org.opengis.cite.ogcapifeatures10.OgcApiFeatures10;
import org.opengis.cite.ogcapifeatures10.conformance.crs.query.crs.CoordinateSystem;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
/**
*
* JsonUtils class.
*
*
* @author Lyn Goltz
*/
public class JsonUtils {
private JsonUtils() {
}
/**
* Parse value ass string.
* @param value to parse, may be null
* @return the value as string, null
if the passed value was
* null
*/
public static String parseAsString(Object value) {
if (value == null)
return null;
return value.toString();
}
/**
* Parses the id of the first feature from the passed json.
* @param collectionItemJson the json document containing the features, never
* null
* @return the parsed id, may be null
if no feature could be found
*/
public static String parseFeatureId(JsonPath collectionItemJson) {
List> features = collectionItemJson.get("features");
if (features == null)
return null;
for (Map feature : features) {
if (feature.containsKey("id"))
return feature.get("id").toString();
}
return null;
}
/**
* Parses the temporal extent from the passed collection.
* @param collection the collection containing the extent to parse, never
* null
* @return the parsed temporal extent, null
if no extent exists
* @throws java.lang.IllegalArgumentException if the number of items in the extent
* invalid
*/
public static TemporalExtent parseTemporalExtent(Map collection) {
Object extent = collection.get("extent");
if (extent == null || !(extent instanceof Map))
return null;
Object spatial = ((Map) extent).get("temporal");
if (spatial == null || !(spatial instanceof List))
return null;
List coords = (List) spatial;
if (coords.size() == 2) {
ZonedDateTime begin = parseAsDate((String) coords.get(0));
ZonedDateTime end = parseAsDate((String) coords.get(1));
return new TemporalExtent(begin, end);
}
throw new IllegalArgumentException("Temporal extent with " + coords.size() + " items is invalid");
}
/**
* Parses the passed string as ISO 8601 date.
* @param dateTime the dateTime to parse, never null
* @return the parsed date, never null
*/
public static ZonedDateTime parseAsDate(String dateTime) {
return ZonedDateTime.parse(dateTime);
}
/**
* Formats the passed string as ISO 8601 date. Example: "2018-02-12T23:20:50Z"
* @param dateTime the dateTime to format, never null
* @return the formatted date, never null
*/
public static String formatDate(ZonedDateTime dateTime) {
return DateTimeFormatter.ISO_INSTANT.format(dateTime);
}
/**
* Formats the passed string as ISO 8601 date. Example: "2018-02-12"
* @param date the dateTime to format, never null
* @return the formatted date, never null
*/
public static String formatDate(LocalDate date) {
return DateTimeFormatter.ISO_DATE.format(date);
}
/**
* Formats the passed string as a period using a start and end time. Example:
* "2018-02-12T00:00:00Z/2018-03-18T12:31:12Z"
* @param beginDateTime the begin dateTime to format, never null
* @param endDateTime the end dateTime to format, never null
* @return the formatted date, never null
*/
public static String formatDateRange(ZonedDateTime beginDateTime, ZonedDateTime endDateTime) {
return formatDate(beginDateTime) + "/" + formatDate(endDateTime);
}
/**
* Formats the passed string as a period using start time and a duration. Example:
* "2018-02-12T00:00:00Z/P1M6DT12H31M12S"
* @param beginDate the begin date to format, never null
* @param endDate the end date to format, never null
* @return the formatted date, never null
*/
public static String formatDateRangeWithDuration(LocalDate beginDate, LocalDate endDate) {
Period betweenDate = Period.between(beginDate, endDate);
return formatDate(beginDate) + "/" + betweenDate;
}
/**
* Parses the spatial extent from the passed collection.
* @param collection the collection containing the extent to parse, never
* null
* @return the parsed bbox, null
if no extent exists
* @throws java.lang.IllegalArgumentException if the number of items in the extent
* invalid
*/
public static BBox parseSpatialExtent(Map collection) {
Object extent = collection.get("extent");
if (extent == null || !(extent instanceof Map))
return null;
Object spatial = ((Map) extent).get("spatial");
if (spatial == null || !(spatial instanceof Map))
return null;
Object bbox = ((Map) spatial).get("bbox");
if (bbox == null || !(bbox instanceof List))
return null;
List bboxesOrCoordinates = (List) bbox;
if (bboxesOrCoordinates.isEmpty())
return null;
if (containsMultipleBboxes(bboxesOrCoordinates)) {
if (bboxesOrCoordinates.isEmpty())
return null;
Object firstBbox = bboxesOrCoordinates.get(0);
if (firstBbox == null || !(firstBbox instanceof List)) {
return null;
}
List coordinatesOfFirstBBox = (List) firstBbox;
return parseBbox(coordinatesOfFirstBBox, (Map) spatial);
}
else {
return parseBbox(bboxesOrCoordinates, (Map) spatial);
}
}
private static BBox parseBbox(List coords, Map spatial) {
if (coords.size() == 4) {
CoordinateSystem crs = parseCrs(spatial);
double minX = parseValueAsDouble(coords.get(0));
double minY = parseValueAsDouble(coords.get(1));
double maxX = parseValueAsDouble(coords.get(2));
double maxY = parseValueAsDouble(coords.get(3));
return new BBox(minX, minY, maxX, maxY, crs);
}
else if (coords.size() == 6) {
throw new IllegalArgumentException(
"BBox with " + coords.size() + " coordinates is currently not supported");
}
throw new IllegalArgumentException("BBox with " + coords.size() + " coordinates is invalid");
}
private static CoordinateSystem parseCrs(Map spatial) {
String crs = parseAsString(spatial.get("crs"));
if (crs != null)
return new CoordinateSystem(crs);
return DEFAULT_CRS;
}
private static boolean containsMultipleBboxes(List bboxes) {
Object first = bboxes.get(0);
return first instanceof List;
}
/**
* Parses all links with 'type' of one of the passed mediaTypes and the 'rel' property
* with the passed value.
* @param links list of all links, never null
* @param mediaTypesToSupport a list of media types the links searched for should
* support, may be empty but never null
* @param expectedRel the expected value of the property 'rel', never
* null
* @return a list of links supporting one of the media types and with the expected
* 'rel' property, may be empty but never null
*/
public static List> findLinksWithSupportedMediaTypeByRel(List> links,
List mediaTypesToSupport, String expectedRel) {
List> alternateLinks = new ArrayList<>();
for (Map link : links) {
Object type = link.get("type");
Object rel = link.get("rel");
if (expectedRel.equals(rel) && isSupportedMediaType(type, mediaTypesToSupport))
alternateLinks.add(link);
}
return alternateLinks;
}
/**
* Parsing the media types which does not have a link woth property 'type' for.
* @param links list of links to search in, never null
* @param mediaTypesToSuppport a list of media types which should be supported, never
* null
* @return the media types which does not have a link for.
*/
public static List findUnsupportedTypes(List> links,
List mediaTypesToSuppport) {
List unsupportedType = new ArrayList<>();
for (String contentMediaType : mediaTypesToSuppport) {
boolean hasLinkForContentType = hasLinkForContentType(links, contentMediaType);
if (!hasLinkForContentType)
unsupportedType.add(contentMediaType);
}
return unsupportedType;
}
/**
* Parses the links without 'rel' or 'type' property.
* @param links list of links to search in, never null
* @param rels Set of relation types, never null
* @return the links without 'rel' or 'type' property
*/
public static List findLinksWithoutRelOrType(List> links, Set rels) {
List linksWithoutRelOrType = new ArrayList<>();
for (Map link : links) {
if (rels.contains(link.get("rel")) && !linkIncludesRelAndType(link))
linksWithoutRelOrType.add((String) link.get("href"));
}
return linksWithoutRelOrType;
}
/**
* Parses the link with 'rel=self'.
* @param links list of links to search in, never null
* @param expectedRel the expected value of the property 'rel', never
* null
* @return the link to itself or null
if no such link exists
*/
public static Map findLinkByRel(List> links, String expectedRel) {
if (links == null)
return null;
for (Map link : links) {
Object rel = link.get("rel");
if (expectedRel.equals(rel))
return link;
}
return null;
}
/**
* Checks if the passed link contains 'rel' and 'type' properties.
* @param link to check, never null
* @return true
if the link contains 'rel' and 'type' properties,
* false
otherwise
*/
public static boolean linkIncludesRelAndType(Map link) {
Object rel = link.get("rel");
Object type = link.get("type");
if (rel != null && type != null)
return true;
return false;
}
/**
* Checks if a property with the passed name exists in the jsonPath.
* @param propertyName name of the property to check, never null
* @param jsonPath to check, never null
* @return true
if the property exists, false
otherwise
*/
public static boolean hasProperty(String propertyName, JsonPath jsonPath) {
return jsonPath.get(propertyName) != null;
}
/**
* Checks if a at least one of the collection in the /collections response has a
* spatial extent.
* @param jsonPath to check, never null
* @return true
at least one of the collection has a spatial extent,
* false
otherwise
*/
public static boolean hasAtLeastOneSpatialFeatureCollection(JsonPath jsonPath) {
List collections = jsonPath.getList("collections");
for (Object collectionObj : collections) {
if (hasAtLeastOneSpatialFeatureCollection((Map) collectionObj))
return true;
}
return false;
}
/**
* Checks if a at least one of the collection in the /collections response has a
* spatial extent.
* @param collection to check, never null
* @return true
at least one of the collection has a spatial extent,
* false
otherwise
*/
public static boolean hasAtLeastOneSpatialFeatureCollection(Map collection) {
Object extent = collection.get("extent");
return hasAtLeastOneSpatialFeatureCollection(extent);
}
/**
* Checks if the extent contains a spatial extent.
* @param extent to check, never null
* @return true
if extent contains a spatial extent, false
* otherwise
*/
public static boolean hasAtLeastOneSpatialFeatureCollection(Object extent) {
if (extent != null && extent instanceof Map) {
Object spatial = ((Map) extent).get("spatial");
if (spatial != null)
return true;
}
return false;
}
/**
* Retrieves the property values as list.
* @param propertyName name of the property, never null
* @param jsonPath the json document to retrieve properties from, never
* null
* @return the property values as list, may be empty but never null
*/
public static List parseAsList(String propertyName, JsonPath jsonPath) {
Object value = jsonPath.get(propertyName);
if (value == null)
return Collections.emptyList();
if (value instanceof String)
return Collections.singletonList((String) value);
return jsonPath.getList(propertyName);
}
/**
* Retrieves the property values as list.
* @param propertyName name of the property, never null
* @param jsonPath the json document to retrieve properties from, never
* null
* @return the property values as list, may be empty but never null
*/
public static List> parseAsListOfMaps(String propertyName, JsonPath jsonPath) {
Object value = jsonPath.get(propertyName);
if (value == null)
return Collections.emptyList();
return jsonPath.getList(propertyName);
}
/**
* Retrieves the property values as list.
* @param propertyName name of the property, never null
* @param json the json map to retrieve properties from, never null
* @return the property values as list, may be empty but never null
*/
public static List parseAsList(String propertyName, Map json) {
Object value = json.get(propertyName);
if (value == null)
return Collections.emptyList();
if (value instanceof String)
return Collections.singletonList((String) value);
return (List) json.get(propertyName);
}
/**
* Collects the number of all returned features by iterating over all 'next' links and
* summarizing the size of features in 'features' array property.
* @param jsonPath the initial collection, never null
* @param maximumLimit the limit parameter value to use, if <= 0 the parameter is
* omitted
* @return the number of all returned features
* @throws java.net.URISyntaxException if the creation of a uri fails
*/
public static int collectNumberOfAllReturnedFeatures(JsonPath jsonPath, int maximumLimit)
throws URISyntaxException {
int numberOfAllReturnedFeatures = parseAsList("features", jsonPath).size();
Map nextLink = findLinkByRel(jsonPath.getList("links"), "next");
int count = 0;
while (nextLink != null && (count <= OgcApiFeatures10.PAGING_LIMIT)) {
String nextUrl = (String) nextLink.get("href");
URI uri = new URI(nextUrl);
RequestSpecification accept = given().log().all().baseUri(nextUrl).accept(GEOJSON_MIME_TYPE);
String[] pairs = uri.getQuery().split("&");
String limitParamFromUri = null;
for (String pair : pairs) {
int idx = pair.indexOf("=");
String key = pair.substring(0, idx);
String value = pair.substring(idx + 1);
if ("limit".equals(key)) {
limitParamFromUri = value;
}
else {
accept.param(key, value);
}
}
if (maximumLimit > 0) {
accept.param("limit", maximumLimit);
}
else if (limitParamFromUri != null) {
accept.param("limit", limitParamFromUri);
}
Response response = accept.when().request(GET);
response.then().statusCode(200);
JsonPath nextJsonPath = response.jsonPath();
int features = parseAsList("features", nextJsonPath).size();
if (features > 0) {
numberOfAllReturnedFeatures += features;
nextLink = findLinkByRel(nextJsonPath.getList("links"), "next");
}
else {
nextLink = null;
}
count++;
}
return numberOfAllReturnedFeatures;
}
/**
* Finds the URL to the resource /collections/{collectionId}/items from the path
* /collections/{collectionId}
* @param rootUri never null
* @param collection the /collections/{collectionId} response, never null
* @return the url to the resource /collections/{collectionId}/items or
* null
*/
public static String findFeaturesUrlForGeoJson(URI rootUri, JsonPath collection) {
List links = collection.get("links");
return findFeaturesUrlForGeoJson(rootUri, links);
}
/**
* Finds the URL to the resource /collections/{collectionId}/items from the path
* /collections
* @param rootUri never null
* @param collection the collection object /collections response, never
* null
* @return the url to the resource /collections/{collectionId}/items or
* null
*/
public static String findFeaturesUrlForGeoJson(URI rootUri, Map collection) {
List links = (List) collection.get("links");
return findFeaturesUrlForGeoJson(rootUri, links);
}
/**
* Finds the URL to the resource /collections/{collectionId}/items/{featureId} from
* the path /collections and creates an valid url to this resource
* @param rootUri never null
* @param collection the /collections/{collectionId} response, never null
* @param featureId id of the feature, never null
* @return the url to the resource /collections/{collectionId}/items or
* null
*/
public static String findFeatureUrlForGeoJson(URI rootUri, JsonPath collection, String featureId) {
List links = collection.get("links");
return findFeatureUrlForGeoJson(rootUri, featureId, links);
}
/**
* Finds the URL to the resource /collections/{collectionId}/items/{featureId} from
* the path /collections and creates an valid url to this resource
* @param rootUri never null
* @param collection the collection object /collections response, never
* null
* @param featureId id of the feature, never null
* @return the url to the resource /collections/{collectionId}/items or
* null
*/
public static String findFeatureUrlForGeoJson(URI rootUri, Map collection, String featureId) {
List links = (List) collection.get("links");
return findFeatureUrlForGeoJson(rootUri, featureId, links);
}
/**
* Parse the geometry property as geometry.
* @param feature to parse, never null
* @param crs the crs of the geometry, may be null
* @return the parsed geometry, null
if the feature has no geometry
* property
* @throws org.locationtech.jts.io.ParseException if the geometry could not be parsed
*/
public static Geometry parseFeatureGeometry(Map feature, CoordinateSystem crs)
throws ParseException {
Map geometry = (Map) feature.get("geometry");
if (geometry == null)
return null;
JSONObject jsonObject = new JSONObject(geometry);
String geomAsString = jsonObject.toJSONString();
GeoJsonReader geoJsonReader = new GeoJsonReader();
Geometry parsedGeometry = geoJsonReader.read(geomAsString);
parsedGeometry.setSRID(crs.getSrid());
return parsedGeometry;
}
private static String findFeaturesUrlForGeoJson(URI rootUri, List links) {
for (Object linkObject : links) {
Map link = (Map) linkObject;
Object rel = link.get("rel");
Object type = link.get("type");
if ("items".equals(rel) && GEOJSON_MIME_TYPE.equals(type)) {
String url = (String) link.get("href");
if (!url.startsWith("http")) {
String path = url;
if (null != rootUri.getScheme() && !rootUri.getScheme().isEmpty())
url = rootUri.getScheme() + ":";
if (null != rootUri.getAuthority() && !rootUri.getAuthority().isEmpty())
url = url + "//" + rootUri.getAuthority();
url = url + path;
if (null != rootUri.getQuery() && !rootUri.getQuery().isEmpty())
url = url + "?" + rootUri.getQuery();
if (null != rootUri.getFragment() && !rootUri.getFragment().isEmpty())
url = url + "#" + rootUri.getFragment();
}
return url;
}
}
return null;
}
private static String findFeatureUrlForGeoJson(URI rootUri, String featureId, List links) {
String featuresUrlForGeoJson = findFeaturesUrlForGeoJson(rootUri, links);
if (featuresUrlForGeoJson != null) {
return createFeatureUrl(featuresUrlForGeoJson, featureId);
}
return null;
}
private static String createFeatureUrl(String getFeatureUrl, String featureId) {
if (getFeatureUrl.indexOf("?") != -1) {
return getFeatureUrl.substring(0, getFeatureUrl.indexOf("?")) + "/" + featureId;
}
else if (getFeatureUrl.matches("^.*\\/items.[a-zA-Z]*$")) {
return getFeatureUrl.substring(0, getFeatureUrl.lastIndexOf(".")) + "/" + featureId
+ getFeatureUrl.substring(getFeatureUrl.lastIndexOf("."));
}
return getFeatureUrl + "/" + featureId;
}
private static boolean isSameMediaType(String mediaType1, String mediaType2) {
if (mediaType1.contains(";") || mediaType2.contains(";")) {
// media types are not case sensitive
String[] components1 = mediaType1.toLowerCase().split(";");
String[] components2 = mediaType2.toLowerCase().split(";");
// type and subtype must match
if (!components1[0].trim().equals(components2[0].trim()))
return false;
Set parameters1 = new HashSet<>();
Set parameters2 = new HashSet<>();
// normalize parameter values and compare them
for (int i = 1; i < components1.length; i++) {
String parameter = components1[i].trim().replace("\"", "");
if (!parameter.isEmpty())
parameters1.add(parameter);
}
for (int i = 1; i < components2.length; i++) {
String parameter = components2[i].trim().replace("\"", "");
if (!parameter.isEmpty())
parameters2.add(parameter);
}
if (parameters1.size() != parameters2.size())
return false;
if (!parameters1.containsAll(parameters2))
return false;
}
else if (!mediaType1.trim().equalsIgnoreCase(mediaType2.trim()))
return false;
return true;
}
private static boolean hasLinkForContentType(List> alternateLinks, String mediaType) {
for (Map alternateLink : alternateLinks) {
Object type = alternateLink.get("type");
if (type instanceof String && isSameMediaType(mediaType, (String) type))
return true;
}
return false;
}
private static boolean isSupportedMediaType(Object type, List mediaTypes) {
for (String mediaType : mediaTypes) {
if (type instanceof String && isSameMediaType(mediaType, (String) type))
return true;
}
return false;
}
private static double parseValueAsDouble(Object value) {
if (value instanceof Integer) {
return ((Integer) value).doubleValue();
}
else if (value instanceof Float) {
return ((Float) value).doubleValue();
}
else if (value instanceof Double) {
return (Double) value;
}
else {
return Double.parseDouble(value.toString());
}
}
}