/**
* Copyright 2013 OpenSocial Foundation
* Copyright 2013 International Business Machines Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Utility library for working with Activity Streams Actions
* Requires underscorejs.
*
* @author James M Snell (jasnell@us.ibm.com)
*/
package com.ibm.common.geojson.as2;
import static com.google.common.base.Preconditions.checkArgument;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Map;
import com.google.common.base.Enums;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.ibm.common.activitystreams.internal.Adapter;
import com.ibm.common.geojson.BoundingBox;
import com.ibm.common.geojson.CRS;
import com.ibm.common.geojson.Feature;
import com.ibm.common.geojson.FeatureCollection;
import com.ibm.common.geojson.GeoMakers;
import com.ibm.common.geojson.GeoObject;
import com.ibm.common.geojson.Geometry;
import com.ibm.common.geojson.Geometry.CoordinateGeometry;
import com.ibm.common.geojson.GeometryCollection;
import com.ibm.common.geojson.LineString;
import com.ibm.common.geojson.MultiLineString;
import com.ibm.common.geojson.MultiPoint;
import com.ibm.common.geojson.MultiPolygon;
import com.ibm.common.geojson.Point;
import com.ibm.common.geojson.Polygon;
import com.ibm.common.geojson.Position;
@SuppressWarnings("rawtypes")
public class GeoAdapter
extends Adapter<GeoObject> {
@Override
public JsonElement serialize(
GeoObject geo,
Type type,
JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.add("type", context.serialize(geo.type(),GeoObject.Type.class));
switch(geo.type()) {
case POINT:
case MULTIPOINT:
case LINESTRING:
case MULTILINESTRING:
case MULTIPOLYGON:
case POLYGON:
CoordinateGeometry c =
(CoordinateGeometry) geo;
obj.add(
"coordinates",
context.serialize(
c.coordinates(),
Iterable.class));
break;
case GEOMETRYCOLLECTION:
GeometryCollection gc =
(GeometryCollection)geo;
obj.add(
"geometries",
context.serialize(
gc.geometries(),
Iterable.class));
break;
case FEATURE:
Feature feature =
(Feature) geo;
if (feature.id() != null)
obj.addProperty("id", feature.id());
Geometry<?,?> geometry = feature.geometry();
Map<String,Object> properties = feature.properties();
if (geometry != null)
obj.add(
"geometry",
context.serialize(feature.geometry()));
if (properties != null)
obj.add(
"properties",
context.serialize(properties));
break;
case FEATURECOLLECTION:
FeatureCollection fc =
(FeatureCollection)geo;
obj.add(
"features",
context.serialize(
fc.features(),
Iterable.class));
break;
default:
break;
}
if (geo.boundingBox() != null) {
BoundingBox bb = geo.boundingBox();
obj.add("bbox", context.serialize(bb, Iterable.class));
}
if (geo.crs() != null) {
CRS crs = geo.crs();
JsonObject crsobj = new JsonObject();
crsobj.addProperty("name", crs.type());
if (crs.size() > 0)
crsobj.add(
"properties",
context.serialize(crs.properties()));
obj.add("crs", crsobj);
}
return obj;
}
private Position toPosition(float[] pos) {
Position.Builder b =
new Position.Builder();
for (int n = 0; n < pos.length; n++) {
if (n == 0) b.northing(pos[n]);
else if (n == 1) b.easting(pos[n]);
else if (n == 2) b.altitude(pos[n]);
else b.additional(pos[n]);
}
return b.get();
}
@Override
public GeoObject deserialize(
JsonElement element,
Type type,
JsonDeserializationContext context)
throws JsonParseException {
GeoObject.Builder geo = null;
checkArgument(element.isJsonObject());
JsonObject obj = element.getAsJsonObject();
checkArgument(obj.has("type"));
GeoObject.Type et =
Enums.getIfPresent(
GeoObject.Type.class,
obj.get("type").getAsString().toUpperCase()).orNull();
checkArgument(et != null);
switch(et) {
case FEATURE:
geo = GeoMakers.feature();
break;
case FEATURECOLLECTION:
geo = GeoMakers.featureCollection();
type = Feature.class;
break;
case GEOMETRYCOLLECTION:
geo = GeoMakers.geometryCollection();
type = Geometry.class;
break;
case LINESTRING:
geo = GeoMakers.linestring();
type = Position.class;
break;
case MULTILINESTRING:
geo = GeoMakers.multiLineString();
type = LineString.class;
break;
case MULTIPOINT:
geo = GeoMakers.multipoint();
type = Position.class;
break;
case MULTIPOLYGON:
geo = GeoMakers.multiPolygon();
type = Polygon.class;
break;
case POINT:
geo = GeoMakers.point();
type = null;
break;
case POLYGON:
geo = GeoMakers.polygon();
type = LineString.class;
break;
}
for (Map.Entry<String,JsonElement> entry : obj.entrySet()) {
JsonElement el = entry.getValue();
String name = entry.getKey();
if ("crs".equals(name)) {
CRS.Builder cb = new CRS.Builder();
JsonObject o = el.getAsJsonObject();
if (o.has("type"))
cb.type(o.get("type").getAsString());
if (o.has("properties")) {
JsonObject p = o.get("properties").getAsJsonObject();
for (Map.Entry<String,JsonElement> e : p.entrySet()) {
cb.set(e.getKey(), context.deserialize(e.getValue(), Object.class));
}
}
geo.crs(cb.get());
} else if ("properties".equals(name)) {
geo.set(
"properties",
context.deserialize(
el,
Map.class));
} else if ("bbox".equals(name)) {
BoundingBox.Builder bb =
new BoundingBox.Builder();
float[] points = context.deserialize(el, float[].class);
bb.add(points);
geo.boundingBox(bb.get());
} else if ("features".equals(name)) {
Feature[] features = context.deserialize(el, Feature[].class);
FeatureCollection.Builder fcb = (FeatureCollection.Builder)geo;
for (Feature f : features)
fcb.add(f);
} else if ("coordinates".equals(name)) {
switch(et) {
case LINESTRING: {
LineString.Builder lsb = (LineString.Builder) geo;
float[][] positions = context.deserialize(el, float[][].class);
boolean ring = ring(positions);
if (ring)
lsb.linearRing();
for (int n = 0; n < positions.length; n++) {
if (!ring || (ring && n < positions.length - 1))
lsb.add(toPosition(positions[n]));
}
break;
}
case MULTIPOINT: {
MultiPoint.Builder lsb = (MultiPoint.Builder) geo;
float[][] positions = context.deserialize(el, float[][].class);
for (float[] pos : positions)
lsb.add(toPosition(pos));
break;
}
case MULTILINESTRING: {
MultiLineString.Builder mlb = (MultiLineString.Builder) geo;
float[][][] positions = context.deserialize(el, float[][][].class);
for (float[][] lines : positions) {
LineString.Builder lsb =
GeoMakers.linestring();
boolean ring = ring(lines);
if (ring)
lsb.linearRing();
for (int n = 0; n < lines.length; n++) {
if (!ring || (ring && n < lines.length - 1))
lsb.add(toPosition(lines[n]));
}
for (float[] pos : lines)
lsb.add(toPosition(pos));
mlb.add(lsb);
}
break;
}
case POLYGON: {
Polygon.Builder mlb = (Polygon.Builder) geo;
float[][][] positions = context.deserialize(el, float[][][].class);
for (float[][] lines : positions) {
LineString.Builder lsb =
GeoMakers.linestring();
for (float[] pos : lines)
lsb.add(toPosition(pos));
mlb.add(lsb);
}
break;
}
case MULTIPOLYGON: {
MultiPolygon.Builder mpb = (MultiPolygon.Builder) geo;
float[][][][] positions = context.deserialize(el, float[][][][].class);
for (float[][][] polygons : positions) {
Polygon.Builder pb = GeoMakers.polygon();
for (float[][] lines : polygons) {
LineString.Builder lsb =
GeoMakers.linestring();
for (float[] pos : lines)
lsb.add(toPosition(pos));
pb.add(lsb);
}
mpb.add(pb);
}
break;
}
case POINT:
Point.Builder pb = (Point.Builder)geo;
float[] position = context.deserialize(el, float[].class);
pb.position(toPosition(position));
break;
default:
break;
}
} else if ("geometries".equals(name)) {
Geometry[] geos = context.deserialize(el, Geometry[].class);
GeometryCollection.Builder fcb = (GeometryCollection.Builder)geo;
for (Geometry<?,?> g : geos)
fcb.add(g);
} else {
if (el.isJsonArray()) {
geo.set(name, context.deserialize(el, Object.class));
} else if (el.isJsonObject()) {
geo.set(name, context.deserialize(el, GeoObject.class));
} else if (el.isJsonPrimitive()) {
JsonPrimitive p = el.getAsJsonPrimitive();
if (p.isBoolean())
geo.set(name, p.getAsBoolean());
else if (p.isNumber())
geo.set(name, p.getAsNumber());
else if (p.isString())
geo.set(name, p.getAsString());
}
}
}
return geo.get();
}
private static boolean ring(float[][] line) {
return ring(first(line),last(line));
}
private static boolean ring(float[] p1, float[] p2) {
return Arrays.equals(p1,p2);
}
private static float[] first(float[][] line) {
if (line.length == 0) return null;
return line[0];
}
private static float[] last(float[][] line) {
if (line.length == 0) return null;
return line[line.length - 1];
}
}