All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mapbox.geojson.Feature Maven / Gradle / Ivy

There is a newer version: 5.9.0-alpha.1
Show newest version
package com.mapbox.geojson;

import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.mapbox.geojson.gson.BoundingBoxTypeAdapter;
import com.mapbox.geojson.gson.GeoJsonAdapterFactory;

import java.io.IOException;

/**
 * This defines a GeoJson Feature object which represents a spatially bound thing. Every Feature
 * object is a GeoJson object no matter where it occurs in a GeoJson text. A Feature object will
 * always have a "TYPE" member with the value "Feature".
 * 

* A Feature object has a member with the name "geometry". The value of the geometry member SHALL be * either a Geometry object or, in the case that the Feature is unlocated, a JSON null value. *

* A Feature object has a member with the name "properties". The value of the properties member is * an object (any JSON object or a JSON null value). *

* If a Feature has a commonly used identifier, that identifier SHOULD be included as a member of * the Feature object through the {@link #id()} method, and the value of this member is either a * JSON string or number. *

* An example of a serialized feature is given below: *

 * {
 *   "TYPE": "Feature",
 *   "geometry": {
 *     "TYPE": "Point",
 *     "coordinates": [102.0, 0.5]
 *   },
 *   "properties": {
 *     "prop0": "value0"
 *   }
 * 
* * @since 1.0.0 */ @Keep public final class Feature implements GeoJson { private static final String TYPE = "Feature"; private final String type; @JsonAdapter(BoundingBoxTypeAdapter.class) private final BoundingBox bbox; private final String id; private final Geometry geometry; private final JsonObject properties; /** * Create a new instance of this class by passing in a formatted valid JSON String. If you are * creating a Feature object from scratch it is better to use one of the other provided static * factory methods such as {@link #fromGeometry(Geometry)}. * * @param json a formatted valid JSON string defining a GeoJson Feature * @return a new instance of this class defined by the values passed inside this static factory * method * @since 1.0.0 */ public static Feature fromJson(@NonNull String json) { GsonBuilder gson = new GsonBuilder(); gson.registerTypeAdapterFactory(GeoJsonAdapterFactory.create()); gson.registerTypeAdapterFactory(GeometryAdapterFactory.create()); Feature feature = gson.create().fromJson(json, Feature.class); // Even thought properties are Nullable, // Feature object will be created with properties set to an empty object, // so that addProperties() would work if (feature.properties() != null) { return feature; } return new Feature(TYPE, feature.bbox(), feature.id(), feature.geometry(), new JsonObject()); } /** * Create a new instance of this class by giving the feature a {@link Geometry}. * * @param geometry a single geometry which makes up this feature object * @return a new instance of this class defined by the values passed inside this static factory * method * @since 1.0.0 */ public static Feature fromGeometry(@Nullable Geometry geometry) { return new Feature(TYPE, null, null, geometry, new JsonObject()); } /** * Create a new instance of this class by giving the feature a {@link Geometry}. You can also pass * in a double array defining a bounding box. * * @param geometry a single geometry which makes up this feature object * @param bbox optionally include a bbox definition as a double array * @return a new instance of this class defined by the values passed inside this static factory * method * @since 1.0.0 */ public static Feature fromGeometry(@Nullable Geometry geometry, @Nullable BoundingBox bbox) { return new Feature(TYPE, bbox, null, geometry, new JsonObject()); } /** * Create a new instance of this class by giving the feature a {@link Geometry} and optionally a * set of properties. * * @param geometry a single geometry which makes up this feature object * @param properties a {@link JsonObject} containing the feature properties * @return a new instance of this class defined by the values passed inside this static factory * method * @since 1.0.0 */ public static Feature fromGeometry(@Nullable Geometry geometry, @Nullable JsonObject properties) { return new Feature(TYPE, null, null, geometry, properties == null ? new JsonObject() : properties); } /** * Create a new instance of this class by giving the feature a {@link Geometry}, optionally a * set of properties, and optionally pass in a bbox. * * @param geometry a single geometry which makes up this feature object * @param bbox optionally include a bbox definition as a double array * @param properties a {@link JsonObject} containing the feature properties * @return a new instance of this class defined by the values passed inside this static factory * method * @since 1.0.0 */ public static Feature fromGeometry(@Nullable Geometry geometry, @Nullable JsonObject properties, @Nullable BoundingBox bbox) { return new Feature(TYPE, bbox, null, geometry, properties == null ? new JsonObject() : properties); } /** * Create a new instance of this class by giving the feature a {@link Geometry}, optionally a * set of properties, and a String which represents the objects id. * * @param geometry a single geometry which makes up this feature object * @param properties a {@link JsonObject} containing the feature properties * @param id common identifier of this feature * @return {@link Feature} * @since 1.0.0 */ public static Feature fromGeometry(@Nullable Geometry geometry, @Nullable JsonObject properties, @Nullable String id) { return new Feature(TYPE, null, id, geometry, properties == null ? new JsonObject() : properties); } /** * Create a new instance of this class by giving the feature a {@link Geometry}, optionally a * set of properties, and a String which represents the objects id. * * @param geometry a single geometry which makes up this feature object * @param properties a {@link JsonObject} containing the feature properties * @param bbox optionally include a bbox definition as a double array * @param id common identifier of this feature * @return {@link Feature} * @since 1.0.0 */ public static Feature fromGeometry(@Nullable Geometry geometry, @NonNull JsonObject properties, @Nullable String id, @Nullable BoundingBox bbox) { return new Feature(TYPE, bbox, id, geometry, properties == null ? new JsonObject() : properties); } Feature(String type, @Nullable BoundingBox bbox, @Nullable String id, @Nullable Geometry geometry, @Nullable JsonObject properties) { if (type == null) { throw new NullPointerException("Null type"); } this.type = type; this.bbox = bbox; this.id = id; this.geometry = geometry; this.properties = properties; } /** * This describes the TYPE of GeoJson geometry this object is, thus this will always return * {@link Feature}. * * @return a String which describes the TYPE of geometry, for this object it will always return * {@code Feature} * @since 1.0.0 */ @NonNull @Override public String type() { return type; } /** * A Feature Collection might have a member named {@code bbox} to include information on the * coordinate range for it's {@link Feature}s. The value of the bbox member MUST be a list of * size 2*n where n is the number of dimensions represented in the contained feature geometries, * with all axes of the most southwesterly point followed by all axes of the more northeasterly * point. The axes order of a bbox follows the axes order of geometries. * * @return a list of double coordinate values describing a bounding box * @since 3.0.0 */ @Nullable @Override public BoundingBox bbox() { return bbox; } /** * A feature may have a commonly used identifier which is either a unique String or number. * * @return a String containing this features unique identification or null if one wasn't given * during creation. * @since 1.0.0 */ @Nullable public String id() { return id; } /** * The geometry which makes up this feature. A Geometry object represents points, curves, and * surfaces in coordinate space. One of the seven geometries provided inside this library can be * passed in through one of the static factory methods. * * @return a single defined {@link Geometry} which makes this feature spatially aware * @since 1.0.0 */ @Nullable public Geometry geometry() { return geometry; } /** * This contains the JSON object which holds the feature properties. The value of the properties * member is a {@link JsonObject} and might be empty if no properties are provided. * * @return a {@link JsonObject} which holds this features current properties * @since 1.0.0 */ @Nullable public JsonObject properties() { return properties; } /** * This takes the currently defined values found inside this instance and converts it to a GeoJson * string. * * @return a JSON string which represents this Feature * @since 1.0.0 */ @Override public String toJson() { Gson gson = new GsonBuilder() .registerTypeAdapterFactory(GeoJsonAdapterFactory.create()) .registerTypeAdapterFactory(GeometryAdapterFactory.create()) .create(); // Empty properties -> should not appear in json string Feature feature = this; if (properties().size() == 0) { feature = new Feature(TYPE, bbox(), id(), geometry(), null); } return gson.toJson(feature); } /** * Gson TYPE adapter for parsing Gson to this class. * * @param gson the built {@link Gson} object * @return the TYPE adapter for this class * @since 3.0.0 */ public static TypeAdapter typeAdapter(Gson gson) { return new Feature.GsonTypeAdapter(gson); } /** * Convenience method to add a String member. * * @param key name of the member * @param value the String value associated with the member * @since 1.0.0 */ public void addStringProperty(String key, String value) { properties().addProperty(key, value); } /** * Convenience method to add a Number member. * * @param key name of the member * @param value the Number value associated with the member * @since 1.0.0 */ public void addNumberProperty(String key, Number value) { properties().addProperty(key, value); } /** * Convenience method to add a Boolean member. * * @param key name of the member * @param value the Boolean value associated with the member * @since 1.0.0 */ public void addBooleanProperty(String key, Boolean value) { properties().addProperty(key, value); } /** * Convenience method to add a Character member. * * @param key name of the member * @param value the Character value associated with the member * @since 1.0.0 */ public void addCharacterProperty(String key, Character value) { properties().addProperty(key, value); } /** * Convenience method to add a JsonElement member. * * @param key name of the member * @param value the JsonElement value associated with the member * @since 1.0.0 */ public void addProperty(String key, JsonElement value) { properties().add(key, value); } /** * Convenience method to get a String member. * * @param key name of the member * @return the value of the member, null if it doesn't exist * @since 1.0.0 */ public String getStringProperty(String key) { JsonElement propertyKey = properties().get(key); return propertyKey == null ? null : propertyKey.getAsString(); } /** * Convenience method to get a Number member. * * @param key name of the member * @return the value of the member, null if it doesn't exist * @since 1.0.0 */ public Number getNumberProperty(String key) { JsonElement propertyKey = properties().get(key); return propertyKey == null ? null : propertyKey.getAsNumber(); } /** * Convenience method to get a Boolean member. * * @param key name of the member * @return the value of the member, null if it doesn't exist * @since 1.0.0 */ public Boolean getBooleanProperty(String key) { JsonElement propertyKey = properties().get(key); return propertyKey == null ? null : propertyKey.getAsBoolean(); } /** * Convenience method to get a Character member. * * @param key name of the member * @return the value of the member, null if it doesn't exist * @since 1.0.0 */ public Character getCharacterProperty(String key) { JsonElement propertyKey = properties().get(key); return propertyKey == null ? null : propertyKey.getAsCharacter(); } /** * Convenience method to get a JsonElement member. * * @param key name of the member * @return the value of the member, null if it doesn't exist * @since 1.0.0 */ public JsonElement getProperty(String key) { return properties().get(key); } /** * Removes the property from the object properties. * * @param key name of the member * @return Removed {@code property} from the key string passed in through the parameter. * @since 1.0.0 */ public JsonElement removeProperty(String key) { return properties().remove(key); } /** * Convenience method to check if a member with the specified name is present in this object. * * @param key name of the member * @return true if there is the member has the specified name, false otherwise. * @since 1.0.0 */ public boolean hasProperty(String key) { return properties().has(key); } /** * Convenience method to check for a member by name as well as non-null value. * * @param key name of the member * @return true if member is present with non-null value, false otherwise. * @since 1.3.0 */ public boolean hasNonNullValueForProperty(String key) { return hasProperty(key) && !getProperty(key).isJsonNull(); } @Override public String toString() { return "Feature{" + "type=" + type + ", " + "bbox=" + bbox + ", " + "id=" + id + ", " + "geometry=" + geometry + ", " + "properties=" + properties + "}"; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof Feature) { Feature that = (Feature) obj; return (this.type.equals(that.type())) && ((this.bbox == null) ? (that.bbox() == null) : this.bbox.equals(that.bbox())) && ((this.id == null) ? (that.id() == null) : this.id.equals(that.id())) && ((this.geometry == null) ? (that.geometry() == null) : this.geometry.equals(that.geometry())) && ((this.properties == null) ? (that.properties() == null) : this.properties.equals(that.properties())); } return false; } @Override public int hashCode() { int hashCode = 1; hashCode *= 1000003; hashCode ^= type.hashCode(); hashCode *= 1000003; hashCode ^= (bbox == null) ? 0 : bbox.hashCode(); hashCode *= 1000003; hashCode ^= (id == null) ? 0 : id.hashCode(); hashCode *= 1000003; hashCode ^= (geometry == null) ? 0 : geometry.hashCode(); hashCode *= 1000003; hashCode ^= (properties == null) ? 0 : properties.hashCode(); return hashCode; } /** * TypeAdapter to serialize/deserialize Feature objects. * * @since 4.6.0 */ static final class GsonTypeAdapter extends TypeAdapter { private volatile TypeAdapter stringTypeAdapter; private volatile TypeAdapter boundingBoxTypeAdapter; private volatile TypeAdapter geometryTypeAdapter; private volatile TypeAdapter jsonObjectTypeAdapter; private final Gson gson; GsonTypeAdapter(Gson gson) { this.gson = gson; } @Override public void write(JsonWriter jsonWriter, Feature object) throws IOException { if (object == null) { jsonWriter.nullValue(); return; } jsonWriter.beginObject(); jsonWriter.name("type"); if (object.type() == null) { jsonWriter.nullValue(); } else { TypeAdapter stringTypeAdapter = this.stringTypeAdapter; if (stringTypeAdapter == null) { stringTypeAdapter = gson.getAdapter(String.class); this.stringTypeAdapter = stringTypeAdapter; } stringTypeAdapter.write(jsonWriter, object.type()); } jsonWriter.name("bbox"); if (object.bbox() == null) { jsonWriter.nullValue(); } else { TypeAdapter boundingBoxTypeAdapter = this.boundingBoxTypeAdapter; if (boundingBoxTypeAdapter == null) { boundingBoxTypeAdapter = gson.getAdapter(BoundingBox.class); this.boundingBoxTypeAdapter = boundingBoxTypeAdapter; } boundingBoxTypeAdapter.write(jsonWriter, object.bbox()); } jsonWriter.name("id"); if (object.id() == null) { jsonWriter.nullValue(); } else { TypeAdapter stringTypeAdapter = this.stringTypeAdapter; if (stringTypeAdapter == null) { stringTypeAdapter = gson.getAdapter(String.class); this.stringTypeAdapter = stringTypeAdapter; } stringTypeAdapter.write(jsonWriter, object.id()); } jsonWriter.name("geometry"); if (object.geometry() == null) { jsonWriter.nullValue(); } else { TypeAdapter geometryTypeAdapter = this.geometryTypeAdapter; if (geometryTypeAdapter == null) { geometryTypeAdapter = gson.getAdapter(Geometry.class); this.geometryTypeAdapter = geometryTypeAdapter; } geometryTypeAdapter.write(jsonWriter, object.geometry()); } jsonWriter.name("properties"); if (object.properties() == null) { jsonWriter.nullValue(); } else { TypeAdapter jsonObjectTypeAdapter = this.jsonObjectTypeAdapter; if (jsonObjectTypeAdapter == null) { jsonObjectTypeAdapter = gson.getAdapter(JsonObject.class); this.jsonObjectTypeAdapter = jsonObjectTypeAdapter; } jsonObjectTypeAdapter.write(jsonWriter, object.properties()); } jsonWriter.endObject(); } @Override public Feature read(JsonReader jsonReader) throws IOException { if (jsonReader.peek() == JsonToken.NULL) { jsonReader.nextNull(); return null; } jsonReader.beginObject(); String type = null; BoundingBox bbox = null; String id = null; Geometry geometry = null; JsonObject properties = null; while (jsonReader.hasNext()) { String name = jsonReader.nextName(); if (jsonReader.peek() == JsonToken.NULL) { jsonReader.nextNull(); continue; } switch (name) { case "type": TypeAdapter strTypeAdapter = this.stringTypeAdapter; if (strTypeAdapter == null) { strTypeAdapter = gson.getAdapter(String.class); this.stringTypeAdapter = strTypeAdapter; } type = strTypeAdapter.read(jsonReader); break; case "bbox": TypeAdapter boundingBoxTypeAdapter = this.boundingBoxTypeAdapter; if (boundingBoxTypeAdapter == null) { boundingBoxTypeAdapter = gson.getAdapter(BoundingBox.class); this.boundingBoxTypeAdapter = boundingBoxTypeAdapter; } bbox = boundingBoxTypeAdapter.read(jsonReader); break; case "id": strTypeAdapter = this.stringTypeAdapter; if (strTypeAdapter == null) { strTypeAdapter = gson.getAdapter(String.class); this.stringTypeAdapter = strTypeAdapter; } id = strTypeAdapter.read(jsonReader); break; case "geometry": TypeAdapter geometryTypeAdapter = this.geometryTypeAdapter; if (geometryTypeAdapter == null) { geometryTypeAdapter = gson.getAdapter(Geometry.class); this.geometryTypeAdapter = geometryTypeAdapter; } geometry = geometryTypeAdapter.read(jsonReader); break; case "properties": TypeAdapter jsonObjectTypeAdapter = this.jsonObjectTypeAdapter; if (jsonObjectTypeAdapter == null) { jsonObjectTypeAdapter = gson.getAdapter(JsonObject.class); this.jsonObjectTypeAdapter = jsonObjectTypeAdapter; } properties = jsonObjectTypeAdapter.read(jsonReader); break; default: jsonReader.skipValue(); } } jsonReader.endObject(); return new Feature(type, bbox, id, geometry, properties); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy