GeoJSONParser.java [src/m/utils] Revision: default  Date:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package m.utils;

import csip.api.server.ServiceException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;

/**
 *
 * @author ktraff
 */
public class GeoJSONParser implements Parser {
    
    enum ShapeType {Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, GeometryCollection};
    protected final Logger LOG = Logger.getLogger(getClass().getSimpleName());
    
    JSONArray layersJSON;
    IGISDatabase db;
    // Used to namespace all database tables during creation/upload.
    String prefix;
    
    public GeoJSONParser(JSONArray lyrs, IGISDatabase db, String prefix) {
        this.layersJSON = lyrs;
        this.db = db;
        this.prefix = prefix;
    }
    
    /**
     * Converts geoJSON layers into its database representation.
     * @return
     * @throws ServiceException
     */
    @Override
    public ArrayList<Layer> parse() throws ServiceException {
        ArrayList<Layer> lyrs = new ArrayList<Layer>();
        try {
            for (int i = 0; i < layersJSON.length(); i++) {
                lyrs.add(parseLayer(layersJSON.getJSONObject(i), i));
            }
        } catch (JSONException ex) {
            throw new csip.api.server.ServiceException(ex);
        }
        return lyrs;
    }
    
    public Layer parseLayer(JSONObject layerJSON, int layerIdx) throws ServiceException {
        Layer lyr;
        try {
            lyr = db.createLayer(prefix + "_" + db.getSessionID() + "_" + layerIdx, layerJSON);
            String type = layerJSON.getString("type");
            if (type.equalsIgnoreCase("FeatureCollection")) {
                JSONArray fts = layerJSON.getJSONArray("features");
                for (int i = 0; i < fts.length(); i++) {
                    lyr.addFeature(parseFeature(fts.getJSONObject(i), layerJSON, lyr));
                }
            }
            else if (type.equalsIgnoreCase("Feature")) {
                lyr.addFeature(parseFeature(layerJSON, layerJSON, lyr));
            }
        } catch (JSONException ex) {
            throw new csip.api.server.ServiceException(ex);
        }
        return lyr;
    }
    
    public Feature parseFeature(JSONObject featureJSON, JSONObject layerJSON, Layer lyr) throws ServiceException {
        try {
            JSONObject crs = generateCRS(db.getDefaultCRS());
            if (hasCRS(featureJSON)) {
                crs = featureJSON.getJSONObject("crs");
            } else if (hasCRS(layerJSON)) {
                crs = layerJSON.getJSONObject("crs");
            }
            LOG.log(Level.INFO, "Chose CRS for layer " + lyr.toString() + ": " + crs.toString());
            featureJSON.put("crs", crs);
            return db.uploadFeature(featureJSON, lyr);
        } catch (JSONException ex) {
            throw new ServiceException(ex);
        }
    }
    
    /**
     * Generates a geoJSON coordinate reference system object.
     * @param urn the OGC CRS URN string: http://geojson.org/geojson-spec.html#coordinate-reference-system-objects
     * @return
     */
    public static JSONObject generateCRS(String urn) throws JSONException {
        JSONObject crs = new JSONObject();
        JSONObject crsProps = new JSONObject();
        crsProps.put("name", urn);
        crs.put("type", "name");
        crs.put("properties", crsProps);
        return crs;
    }
    
    public static boolean hasCRS(JSONObject geoJSON) {
        return geoJSON.has("crs");
    }
    
    public static ShapeType getShape(JSONObject layerJSON) throws JSONException {
        ShapeType shapeType = null;
        String type = layerJSON.getString("type");
        if (type.equalsIgnoreCase("Feature")) {
            shapeType = ShapeType.valueOf(layerJSON.getJSONObject("geometry").getString("type"));
        }
        else if (type.equalsIgnoreCase("FeatureCollection")) {
            JSONArray fts = layerJSON.getJSONArray("features");
            for (int i = 0; i < fts.length(); i++) {
                String featureType = fts.getJSONObject(i).getJSONObject("geometry").getString("type");
                if (shapeType == null) {
                    type = featureType;
                }
                
                if (!type.equalsIgnoreCase(featureType)) {
                    if (isMulti(featureType, type)) {
                        type = multi(type);
                    }
                    else {
                        return ShapeType.GeometryCollection;
                    }
                }
            }
            shapeType = ShapeType.valueOf(type);
        }
        else {  // Geometry of some kind.
            shapeType = ShapeType.valueOf(type);
        }
        Logger.getLogger(GeoJSONParser.class.getName()).log(Level.INFO, "Shape type found: " + shapeType);
        return shapeType;
    }
    
    public static boolean isMulti(String typeOne, String typeTwo) {
        if (typeOne.toLowerCase().contains("multi") || typeTwo.toLowerCase().contains("multi")) {
            String t1 = typeOne.toLowerCase().replace("multi", "");
            String t2 = typeOne.toLowerCase().replace("multi", "");
            return t1.equals(t2);
        }
        return false;
    }
    
    public static String multi(String shapeType) {
        return "Multi" + shapeType;
    }
    
    public static String getSrid(JSONObject layerJSON) {
        try {
            // Note: according to the geoJSON specification, the "crs" attribute
            // must always be in the top-level geojson object.
            JSONObject crs = layerJSON.getJSONObject("crs");
            return crs.getJSONObject("properties").getString("name");
        } catch (JSONException ex) {
            Logger.getLogger(GeoJSONParser.class.getName()).log(Level.WARNING, "Couldn't find projection");
            return null;
        }
    }
    
    public static int getSRIDInt(JSONObject json) throws JSONException {
        String name = getSrid(json);
        if (name == null) {
            return 0;
        }
        return getSRIDInt(name);
    }
    
    public static int getSRIDInt(String name) throws JSONException {
        String [] parts = name.split(":");
        return Integer.parseInt(parts[1]);
    }
    
}