nl.b3p.viewer.stripes.LayarActionBean.java Source code

Java tutorial

Introduction

Here is the source code for nl.b3p.viewer.stripes.LayarActionBean.java

Source

/*
 * Copyright (C) 2011-2013 B3Partners B.V.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package nl.b3p.viewer.stripes;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import java.io.StringReader;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.StreamingResolution;
import net.sourceforge.stripes.action.StrictBinding;
import net.sourceforge.stripes.action.UrlBinding;
import net.sourceforge.stripes.validation.Validate;
import nl.b3p.csw.jaxb.gml.MultiGeometry;
import nl.b3p.viewer.config.ClobElement;
import nl.b3p.viewer.config.services.LayarService;
import nl.b3p.viewer.config.services.LayarSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureIterator;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.referencing.CRS;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.stripesstuff.stripersist.Stripersist;

/**
 * @author Roy Braam
 */
@UrlBinding("/action/layar")
@StrictBinding
public class LayarActionBean implements ActionBean {
    private static final Log log = LogFactory.getLog(LayarActionBean.class);
    private static final Integer TIMEOUT = 10000;
    private static final Integer DEFAULT_RADIUS = 5000;
    private static Integer MAX_FEATURES = 500;
    private static GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
    private static CoordinateReferenceSystem DEFAULT_CRS;
    private static CoordinateReferenceSystem layarCRS;
    private static FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
    /**
     * Init fallback CRS and the LayarCrs.
     */
    static {
        try {
            layarCRS = CRS.decode("EPSG:4326");
            DEFAULT_CRS = CRS.decode("EPSG:28992");
        } catch (Exception e) {
            log.error("Error while getting CRS.", e);
        }
    }

    private ActionBeanContext context;
    /**
     * Layar request param's
     * see: http://layar.com/documentation/browser/api/getpois-request/
     */
    @Validate(required = true)
    private String lon;
    @Validate(required = true)
    private String lat;
    @Validate
    private String countryCode;
    @Validate
    private String userId;
    @Validate
    private Integer radius;
    @Validate(required = true)
    private String layerName;
    @Validate
    private String version;
    @Validate
    private Integer accuracy;
    @Validate
    private String lang;
    //action: update or refresh
    @Validate
    private String action;

    /**
     * Get's the LayarService by the given name.
     * Loads features with the LayarSources and makes the hotspots.
     */
    public Resolution json() throws JSONException {
        String error = "";
        Integer errorCode = 0;

        JSONObject root = new JSONObject();
        root.put("layar", this.layerName);

        LayarService layarService = (LayarService) Stripersist.getEntityManager()
                .createQuery("from LayarService where name=:n").setParameter("n", this.layerName).getSingleResult();

        if (this.radius == null) {
            this.radius = DEFAULT_RADIUS;
        }
        if (layarService != null) {
            root.put("layer", layarService.getName());
            List<LayarSource> layarSources = layarService.getLayarSources();
            Iterator<LayarSource> lit = layarSources.iterator();
            JSONArray hotspots = new JSONArray();
            while (lit.hasNext() && MAX_FEATURES > hotspots.length()) {
                LayarSource layarSource = lit.next();
                //layarSource.getFeatureType().
                FeatureSource fs = null;
                FeatureIterator<SimpleFeature> it = null;
                if (layarSource.getFeatureType() != null) {
                    try {
                        fs = layarSource.getFeatureType().openGeoToolsFeatureSource(TIMEOUT);
                        CoordinateReferenceSystem featureCrs = getCRS(fs);
                        //create filter
                        Filter filter = createFilter(fs, featureCrs);
                        Query q = new Query(fs.getName().toString(), filter);
                        q.setMaxFeatures(MAX_FEATURES - hotspots.length());
                        it = fs.getFeatures(q).features();
                        while (it.hasNext()) {
                            SimpleFeature f = it.next();
                            try {
                                JSONObject hotspot = createHotspot(f, layarSource);
                                hotspots.put(hotspot);
                            } catch (Exception e) {
                                log.error("Error creating hotspot", e);
                            }
                        }
                    } catch (Exception e) {
                        log.error("Error while retrieving features ", e);
                        error = "Error while retrieving features: " + e.getMessage();
                    } finally {
                        if (it != null) {
                            it.close();
                        }
                        if (fs != null) {
                            fs.getDataStore().dispose();
                        }
                    }
                }

            }
            root.put("hotspots", hotspots);
        }
        root.put("errorString", error);
        if (error.length() > 0) {
            //if error, errorCode must be at least 20
            if (errorCode < 20) {
                errorCode = 20;
            }
        }
        root.put("errorCode", errorCode);

        root.put("radius", this.radius);
        return new StreamingResolution("application/json", new StringReader(root.toString()));
    }

    /**
     * Create the filter for retrieving the features.
     * @param fs 
     * @param featureCRS The CRS of the features.
     * @return
     * @throws FactoryException
     * @throws MismatchedDimensionException
     * @throws TransformException 
     */
    private Filter createFilter(FeatureSource fs, CoordinateReferenceSystem featureCRS)
            throws FactoryException, MismatchedDimensionException, TransformException {
        //transform to feature CRS
        Point requestPoint = geometryFactory
                .createPoint(new Coordinate(Double.parseDouble(lon), Double.parseDouble(lat)));
        Geometry targetGeometry = transform(requestPoint, layarCRS, featureCRS);

        String geomAttr = fs.getSchema().getGeometryDescriptor().getLocalName();
        Filter f;
        f = ff.intersects(ff.property(geomAttr), ff.literal(targetGeometry.buffer(this.radius)));
        return f;

    }

    /**
     * Get the CRS for this FeatureSource, if not found return the DEFAULT_CRS
     */
    private CoordinateReferenceSystem getCRS(FeatureSource fs) {
        CoordinateReferenceSystem crs = fs.getSchema().getCoordinateReferenceSystem();
        if (crs == null) {
            log.warn("No Crs found for FeatureType: " + fs.getSchema().getName() + " make use of default crs: "
                    + DEFAULT_CRS.toString());
            crs = DEFAULT_CRS;
        }
        return crs;
    }

    /**
     * Create the hotspot for the given feature
     * @param feature the feature that is used to make the hotspot
     * @param layarSource the layarSource that where the format is defined
     * @return JSONObject that represents the hotspot
     * @throws JSONException
     * @throws FactoryException
     * @throws MismatchedDimensionException
     * @throws TransformException
     * @throws Exception 
     */
    private JSONObject createHotspot(SimpleFeature feature, LayarSource layarSource)
            throws JSONException, FactoryException, MismatchedDimensionException, TransformException, Exception {
        JSONObject hotspot = new JSONObject();
        hotspot.put("id", layarSource.getFeatureType().getTypeName() + feature.getID());
        hotspot.put("anchor", createAnchor(feature));
        hotspot.put("text", createText(feature, layarSource));
        if (layarSource.getDetails().get("imageURL") != null) {
            hotspot.put("imageURL", replaceValuesInString(layarSource.getDetails().get("imageURL"), feature));
        }
        return hotspot;
    }

    /**
     * Create the geo anchor for the hotspot.
     * @param f the feature
     * @return
     * @throws FactoryException
     * @throws MismatchedDimensionException
     * @throws TransformException
     * @throws Exception 
     */
    private JSONObject createAnchor(SimpleFeature f)
            throws FactoryException, MismatchedDimensionException, TransformException, Exception {
        if (f.getDefaultGeometry() == null) {
            throw new Exception("No geometry found for feature: " + f.getID());
        }
        JSONObject anchor = new JSONObject();
        Geometry geom = (Geometry) f.getDefaultGeometry();
        Point featurePoint;
        if (geom instanceof Point) {
            featurePoint = (Point) geom;
        } else {
            featurePoint = geom.getCentroid();
        }
        CoordinateReferenceSystem sourceCRS = f.getDefaultGeometryProperty().getDescriptor()
                .getCoordinateReferenceSystem();
        Point latLonPoint = (Point) transform(featurePoint, sourceCRS, layarCRS);

        JSONObject geolocation = new JSONObject();
        geolocation.put("lat", latLonPoint.getY());
        geolocation.put("lon", latLonPoint.getX());
        anchor.put("geolocation", geolocation);
        return anchor;
    }

    /**
     * Create the text node in the hotspot.
     * @param f 
     * @param layarSource 
     * @return
     * @throws JSONException
     * @throws Exception 
     */
    private JSONObject createText(SimpleFeature f, LayarSource layarSource) throws JSONException, Exception {
        JSONObject text = new JSONObject();
        Map<String, ClobElement> details = layarSource.getDetails();
        if (details.get("text.title") == null) {
            throw new Exception("text.title must be configured");
        }
        text.put("title", replaceValuesInString(details.get("text.title"), f));
        if (details.get("text.description") != null) {
            text.put("description", replaceValuesInString(details.get("text.description"), f));
        }
        if (details.get("text.footnote") != null) {
            text.put("footnote", replaceValuesInString(details.get("text.footnote"), f));
        }
        return text;
    }

    private String replaceValuesInString(ClobElement clob, SimpleFeature f) throws Exception {
        return replaceValuesInString(clob.toString(), f);
    }

    /**
     * Replace all the [attributename] in the given string with the values in the SimpleFeature
     * @param string
     * @param f
     * @return
     * @throws Exception 
     */
    private String replaceValuesInString(String string, SimpleFeature f) throws Exception {
        if (string == null) {
            return null;
        }
        if (!string.contains("[") && !string.contains("]")) {
            return string;
        }
        StringBuilder url = new StringBuilder(string);

        int begin = -1;
        int end = -1;
        for (int i = 0; i < url.length(); i++) {
            char c = url.charAt(i);
            if (c == '[') {
                if (begin == -1) {
                    begin = i;
                } else {
                    throw new Exception("Configuration of \"" + string + "\" not correct. ']' missing .");
                }
            } else if (c == ']') {
                end = i;
                if (begin != -1 && end != -1) {
                    String attribName = url.substring(begin + 1, end);
                    Object value = null;
                    if (attribName == null || attribName.length() == 0) {
                        value = "";
                    } else {
                        value = f.getAttribute(attribName);
                    }
                    if (value == null) {
                        value = "";
                    }
                    url.replace(begin, end + 1, value.toString().trim());
                    i = begin;
                    begin = -1;
                    end = -1;
                } else {
                    throw new Exception("Configuration of \"" + string + "\" not correct. Missing '[' .");
                }
            } else if (i == url.length() - 1 && begin != -1) {
                throw new Exception("Configuration of \"" + string + "\" not correct. Missing ']' .");
            }
        }
        return url.toString();
    }

    /**
     * Transform geometry to an CRS
     * @param sourceGeometry the geometry
     * @param fromCrs the CRS of the geometry
     * @param toCrs the new CRS of the geometry
     * @return
     * @throws FactoryException
     * @throws MismatchedDimensionException
     * @throws TransformException 
     */
    private static Geometry transform(Geometry sourceGeometry, CoordinateReferenceSystem fromCrs,
            CoordinateReferenceSystem toCrs)
            throws FactoryException, MismatchedDimensionException, TransformException {
        MathTransform transform = CRS.findMathTransform(fromCrs, toCrs);
        Geometry targetGeometry = JTS.transform(sourceGeometry, transform);
        return targetGeometry;
    }

    //<editor-fold defaultstate="collapsed" desc="Getters and Setters">
    @Override
    public ActionBeanContext getContext() {
        return context;
    }

    @Override
    public void setContext(ActionBeanContext context) {
        this.context = context;
    }

    public String getLon() {
        return lon;
    }

    public void setLon(String lon) {
        this.lon = lon;
    }

    public String getLat() {
        return lat;
    }

    public void setLat(String lat) {
        this.lat = lat;
    }

    public String getCountryCode() {
        return countryCode;
    }

    public void setCountryCode(String countryCode) {
        this.countryCode = countryCode;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public Integer getRadius() {
        return radius;
    }

    public void setRadius(Integer radius) {
        this.radius = radius;
    }

    public String getLayerName() {
        return layerName;
    }

    public void setLayerName(String layerName) {
        this.layerName = layerName;
    }

    public Integer getAccuracy() {
        return accuracy;
    }

    public void setAccuracy(Integer accuracy) {
        this.accuracy = accuracy;
    }

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }
    //</editor-fold>   
}