Package org.geomajas.internal.service

Source Code of org.geomajas.internal.service.GeoServiceImpl

/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2011 Geosparc nv, http://www.geosparc.com/, Belgium.
*
* The program is available in open source according to the GNU Affero
* General Public License. All contributions in this program are covered
* by the Geomajas Contributors License Agreement. For full licensing
* details, see LICENSE.txt in the project root.
*/
package org.geomajas.internal.service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PostConstruct;

import org.geomajas.geometry.Bbox;
import org.geomajas.geometry.Crs;
import org.geomajas.geometry.CrsTransform;
import org.geomajas.global.CrsInfo;
import org.geomajas.global.CrsTransformInfo;
import org.geomajas.global.ExceptionCode;
import org.geomajas.global.GeomajasException;
import org.geomajas.internal.service.crs.CrsFactory;
import org.geomajas.layer.LayerException;
import org.geomajas.layer.feature.InternalFeature;
import org.geomajas.service.GeoService;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.vividsolutions.jts.algorithm.InteriorPointArea;
import com.vividsolutions.jts.algorithm.InteriorPointLine;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;

/**
* Collection of utility functions concerning geometries.
*
* @author Joachim Van der Auwera
* @author Jan De Moerloose
* @author Pieter De Graef
*/
@Component()
public final class GeoServiceImpl implements GeoService {

  private final Logger log = LoggerFactory.getLogger(GeoServiceImpl.class);

  @Autowired(required = false)
  private Map<String, CrsInfo> crsDefinitions;

  @Autowired(required = false)
  private Map<String, CrsTransformInfo> crsTransformDefinitions;

  private Map<String, Crs> crsCache = new ConcurrentHashMap<String, Crs>();

  private Map<String, CrsTransform> transformCache = new ConcurrentHashMap<String, CrsTransform>();

  private static final Map<Class<? extends Geometry>, Geometry> EMPTY_GEOMETRIES =
    new HashMap<Class<? extends Geometry>, Geometry>();

  @PostConstruct
  protected void postConstruct() throws GeomajasException {
    if (null != crsDefinitions) {
      for (CrsInfo crsInfo : crsDefinitions.values()) {
        try {
          CoordinateReferenceSystem crs = CRS.parseWKT(crsInfo.getCrsWkt());
          String code = crsInfo.getKey();
          crsCache.put(code, CrsFactory.getCrs(code, crs));
        } catch (FactoryException e) {
          throw new GeomajasException(e, ExceptionCode.CRS_DECODE_FAILURE_FOR_MAP, crsInfo.getKey());
        }
      }
    }
    if (null != crsTransformDefinitions) {
      for (CrsTransformInfo crsTransformInfo : crsTransformDefinitions.values()) {
        String key = getTransformKey(crsTransformInfo);
        transformCache.put(key, getCrsTransform(key, crsTransformInfo));
      }
    }
    GeometryFactory factory = new GeometryFactory();
    EMPTY_GEOMETRIES.put(Point.class, factory.createPoint((Coordinate) null));
    EMPTY_GEOMETRIES.put(LineString.class, factory.createLineString((Coordinate[]) null));
    EMPTY_GEOMETRIES.put(Polygon.class, factory.createPolygon(null, null));
    EMPTY_GEOMETRIES.put(MultiPoint.class, factory.createMultiPoint((Coordinate[]) null));
    EMPTY_GEOMETRIES.put(MultiLineString.class, factory.createMultiLineString((LineString[]) null)); // cast needed!
    EMPTY_GEOMETRIES.put(MultiPolygon.class, factory.createMultiPolygon((Polygon[]) null)); // cast needed!
    EMPTY_GEOMETRIES.put(Geometry.class, factory.createGeometryCollection(null));
  }

  private String getTransformKey(CrsTransformInfo crsTransformInfo) {
    return crsTransformInfo.getSource() + "->" + crsTransformInfo.getTarget();
  }

  private String getTransformKey(Crs source, Crs target) {
    return source.getId() + "->" + target.getId();
  }

  private CrsTransform getCrsTransform(String key, CrsTransformInfo crsTransformInfo) throws GeomajasException {
    Crs source = getCrs2(crsTransformInfo.getSource());
    Crs target = getCrs2(crsTransformInfo.getTarget());

    MathTransform mathTransform = getBaseMathTransform(source, target);

    return new CrsTransformImpl(key, source, target, mathTransform, crsTransformInfo.getTransformableArea());
  }

  private static Geometry createEmptyGeometryForClass(Class<? extends Geometry> geomClass) {
    Geometry empty = EMPTY_GEOMETRIES.get(geomClass);
    if (empty == null) {
      empty = EMPTY_GEOMETRIES.get(Geometry.class);
    }
    return empty;
  }

  public CoordinateReferenceSystem getCrs(String crs) throws LayerException {
    return getCrs2(crs);
  }

  /** @{inheritDoc} */
  public Crs getCrs2(String crs) throws LayerException {
    try {
      Crs res = crsCache.get(crs);
      if (null == res) {
        res = CrsFactory.getCrs(crs, CRS.decode(crs));
        crsCache.put(crs, res);
      }
      return res;
    } catch (NoSuchAuthorityCodeException e) {
      throw new LayerException(e, ExceptionCode.CRS_DECODE_FAILURE_FOR_MAP, crs);
    } catch (FactoryException e) {
      throw new LayerException(e, ExceptionCode.CRS_DECODE_FAILURE_FOR_MAP, crs);
    }
  }

  /**
   * Isn't there a method for this in GeoTools?
   *
   * @param crs
   *            CRS string in the form of 'EPSG:<srid>'.
   * @return SRID as integer.
   */
  public int getSridFromCrs(String crs) {
    int crsInt;
    if (crs.indexOf(':') != -1) {
      crsInt = Integer.parseInt(crs.substring(crs.indexOf(':') + 1));
    } else {
      try {
        crsInt = Integer.parseInt(crs);
      } catch (NumberFormatException e) {
        crsInt = 0;
      }
    }
    return crsInt;
  }

  /** @{inheritDoc} */
  public String getCodeFromCrs(Crs crs) {
    return crs.getId();
  }

  /** @{inheritDoc} */
  public String getCodeFromCrs(CoordinateReferenceSystem crs) {
    return "EPSG:" + getSridFromCrs(crs);
  }

  /**
   * Unreliable but works if srids are same as EPSG numbers.
   *
   * @param crs
   *            reference system of EPSG type.
   * @return SRID as integer.
   */
  public int getSridFromCrs(CoordinateReferenceSystem crs) {
    return getSridFromCrs(crs.getIdentifiers().iterator().next().toString());
  }

  private MathTransform getBaseMathTransform(Crs sourceCrs, Crs targetCrs) throws GeomajasException {
    try {
      MathTransform transform;
      try {
        transform = CRS.findMathTransform(sourceCrs, targetCrs);
      } catch (Exception e) {
        transform = CRS.findMathTransform(sourceCrs, targetCrs, true);
      }
      return transform;
    } catch (FactoryException fe) {
      throw new GeomajasException(fe, ExceptionCode.CRS_TRANSFORMATION_NOT_POSSIBLE, sourceCrs.getId(),
          targetCrs.getId());
    }
  }

  /** @{inheritDoc} */
  public MathTransform findMathTransform(CoordinateReferenceSystem sourceCrs, CoordinateReferenceSystem targetCrs)
      throws GeomajasException {
    return getCrsTransform(getCrs2(getCodeFromCrs(sourceCrs)), getCrs2(getCodeFromCrs(targetCrs)));
  }

  /** @{inheritDoc} */
  public CrsTransform getCrsTransform(String sourceCrs, String targetCrs) throws GeomajasException {
    return getCrsTransform(getCrs2(sourceCrs), getCrs2(targetCrs));
  }

  /** @{inheritDoc} */
  public CrsTransform getCrsTransform(CoordinateReferenceSystem sourceCrs, CoordinateReferenceSystem targetCrs)
      throws GeomajasException {
    Crs source, target;
    if (sourceCrs instanceof Crs) {
      source = (Crs) sourceCrs;
    } else {
      source = getCrs2(getCodeFromCrs(sourceCrs));
    }
    if (targetCrs instanceof Crs) {
      target = (Crs) targetCrs;
    } else {
      target = getCrs2(getCodeFromCrs(targetCrs));
    }
    return getCrsTransform(source, target);
  }

  /** @{inheritDoc} */
  public CrsTransform getCrsTransform(Crs sourceCrs, Crs targetCrs) throws GeomajasException {
    String key = getTransformKey(sourceCrs, targetCrs);
    CrsTransform transform = transformCache.get(key);
    if (null == transform) {
      MathTransform mathTransform = getBaseMathTransform(sourceCrs, targetCrs);

      // as there was no transformable area configured, try to build it instead
      Envelope transformableArea = null;
      try {
        org.opengis.geometry.Envelope ogEnvelope = CRS.getEnvelope(targetCrs);
        if (null != ogEnvelope) {
          Envelope envelope = new Envelope(ogEnvelope.getLowerCorner().getCoordinate()[0], ogEnvelope
              .getUpperCorner().getCoordinate()[0], ogEnvelope.getLowerCorner().getCoordinate()[1],
              ogEnvelope.getUpperCorner().getCoordinate()[1]);
          log.info("CRS " + targetCrs.getId() + " envelope " + envelope);
          ReferencedEnvelope refEnvelope = new ReferencedEnvelope(envelope, targetCrs);
          transformableArea = refEnvelope.transform(sourceCrs, true);
          log.info("transformable area for " + key + " is " + transformableArea);
        }
      } catch (MismatchedDimensionException mde) {
        log.warn("Cannot build transformableArea for CRS transformation between " + sourceCrs.getId() + " and "
            + targetCrs.getId() + ", " + mde.getMessage());
      } catch (TransformException te) {
        log.warn("Cannot build transformableArea for CRS transformation between " + sourceCrs.getId() + " and "
            + targetCrs.getId() + ", " + te.getMessage());
      } catch (FactoryException fe) {
        log.warn("Cannot build transformableArea for CRS transformation between " + sourceCrs.getId() + " and "
            + targetCrs.getId() + ", " + fe.getMessage());
      }

      transform = new CrsTransformImpl(key, sourceCrs, targetCrs, mathTransform, transformableArea);
      transformCache.put(key, transform);
    }
    return transform;
  }

  /** @{inheritDoc} */
  @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "REC_CATCH_EXCEPTION")
  public Geometry transform(Geometry source, CrsTransform crsTransform) {
    try {
      if (crsTransform.isTransforming()) {
        Geometry transformableArea = crsTransform.getTransformableGeometry();
        if (null != transformableArea) {
          source = source.intersection(transformableArea);
        }
        return JTS.transform(source, crsTransform);
      } else {
        return source;
      }
    } catch (Exception e) { // typically TopologyException, TransformException or FactoryException, but be safe
      log.warn("Problem during transformation " + crsTransform.getId() + "of " + source
          + ", maybe you need to configure the transformable area using a CrsTransformInfo object for this "
          + "transformation. Object replaced by empty Envelope.", e);
      return createEmptyGeometryForClass(source.getClass());
    }
  }

  /** @{inheritDoc} */
  public Geometry transform(Geometry geometry, Crs sourceCrs, Crs targetCrs) throws GeomajasException {
    if (sourceCrs == targetCrs) { // NOPMD
      // only works when the caching of the CRSs works
      return geometry;
    }

    CrsTransform crsTransform = getCrsTransform(sourceCrs, targetCrs);
    return transform(geometry, crsTransform);
  }

  /** @{inheritDoc} */
  public Geometry transform(Geometry geometry, String sourceCrs, String targetCrs) throws GeomajasException {
    if (sourceCrs.equals(targetCrs)) {
      return geometry;
    }

    CrsTransform crsTransform = getCrsTransform(sourceCrs, targetCrs);
    return transform(geometry, crsTransform);
  }

  /** @{inheritDoc} */
  public Geometry transform(Geometry geometry, CoordinateReferenceSystem sourceCrs,
      CoordinateReferenceSystem targetCrs) throws GeomajasException {
    if (sourceCrs == targetCrs) { // NOPMD
      // only works when the caching of the CRSs works
      return geometry;
    }
    Crs source, target;
    if (sourceCrs instanceof Crs) {
      source = (Crs) sourceCrs;
    } else {
      source = getCrs2(getCodeFromCrs(sourceCrs));
    }
    if (targetCrs instanceof Crs) {
      target = (Crs) targetCrs;
    } else {
      target = getCrs2(getCodeFromCrs(targetCrs));
    }

    CrsTransform crsTransform = getCrsTransform(source, target);
    return transform(geometry, crsTransform);
  }

  /** @{inheritDoc} */
  @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "REC_CATCH_EXCEPTION")
  public Bbox transform(Bbox source, CrsTransform crsTransform) {
    try {
      if (crsTransform.isTransforming()) {
        Envelope envelope = new Envelope(source.getX(), source.getMaxX(), source.getY(), source.getMaxY());
        Envelope transformableArea = crsTransform.getTransformableEnvelope();
        if (null != transformableArea) {
          envelope = envelope.intersection(transformableArea);
        }
        if (envelope.isNull()) {
          return new Bbox();
        } else {
          ReferencedEnvelope refEnvelope = new ReferencedEnvelope(envelope, crsTransform.getSource());
          envelope = refEnvelope.transform(crsTransform.getTarget(), true);
          return new Bbox(envelope.getMinX(), envelope.getMinY(), envelope.getWidth(), envelope.getHeight());
        }
      } else {
        return source;
      }
    } catch (Exception e) { // typically TopologyException, TransformException or FactoryException, but be safe
      log.warn("Problem during transformation " + crsTransform.getId() + "of " + source
          + ", maybe you need to configure the transformable area using a CrsTransformInfo object for this "
          + "transformation. Object replaced by empty Bbox.", e);
      return new Bbox();
    }
  }

  /** @{inheritDoc} */
  public Bbox transform(Bbox bbox, Crs sourceCrs, Crs targetCrs) throws GeomajasException {
    if (sourceCrs == targetCrs) { // NOPMD
      // only works when the caching of the CRSs works
      return bbox;
    }

    CrsTransform crsTransform = getCrsTransform(sourceCrs, targetCrs);
    return transform(bbox, crsTransform);
  }

  /** @{inheritDoc} */
  public Bbox transform(Bbox bbox, String sourceCrs, String targetCrs) throws GeomajasException {
    if (sourceCrs.equals(targetCrs)) {
      // only works when the caching of the CRSs works
      return bbox;
    }

    CrsTransform crsTransform = getCrsTransform(sourceCrs, targetCrs);
    return transform(bbox, crsTransform);
  }

  /** @{inheritDoc} */
  @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "REC_CATCH_EXCEPTION")
  public Envelope transform(Envelope source, CrsTransform crsTransform) {
    try {
      if (crsTransform.isTransforming()) {
        Envelope transformableArea = crsTransform.getTransformableEnvelope();
        if (null != transformableArea) {
          source = source.intersection(transformableArea);
        }
        if (source.isNull()) {
          return source;
        } else {
          ReferencedEnvelope refEnvelope = new ReferencedEnvelope(source, crsTransform.getSource());
          return refEnvelope.transform(crsTransform.getTarget(), true);
        }
      } else {
        return source;
      }
    } catch (Exception e) { // typically TopologyException, TransformException or FactoryException, but be safe
      log.warn("Problem during transformation " + crsTransform.getId() + "of " + source
          + ", maybe you need to configure the transformable area using a CrsTransformInfo object for this "
          + "transformation. Object replaced by empty Envelope.", e);
      return new Envelope();
    }
  }

  /** @{inheritDoc} */
  public Envelope transform(Envelope source, Crs sourceCrs, Crs targetCrs) throws GeomajasException {
    if (sourceCrs == targetCrs) { // NOPMD
      // only works when the caching of the CRSs works
      return source;
    }

    CrsTransform crsTransform = getCrsTransform(sourceCrs, targetCrs);
    return transform(source, crsTransform);
  }

  /** @{inheritDoc} */
  public Envelope transform(Envelope source, String sourceCrs, String targetCrs) throws GeomajasException {
    if (sourceCrs.equals(targetCrs)) {
      // only works when the caching of the CRSs works
      return source;
    }

    CrsTransform crsTransform = getCrsTransform(sourceCrs, targetCrs);
    return transform(source, crsTransform);
  }

  /** @{inheritDoc} */
  public Coordinate transform(Coordinate source, CrsTransform crsTransform) {
    try {
      if (crsTransform.isTransforming()) {
        Envelope transformableArea = crsTransform.getTransformableEnvelope();
        if (null == transformableArea || transformableArea.contains(source)) {
          return JTS.transform(source, new Coordinate(), crsTransform);
        }
        return null;
      } else {
        return source;
      }
    } catch (Exception e) { // typically TopologyException, TransformException or FactoryException, but be safe
      log.warn("Problem during transformation " + crsTransform.getId() + "of " + source
          + ", maybe you need to configure the transformable area using a CrsTransformInfo object for this "
          + "transformation. Object replaced by empty Envelope.", e);
      return null;
    }
  }

  /** @{inheritDoc} */
  public Coordinate transform(Coordinate source, Crs sourceCrs, Crs targetCrs) throws GeomajasException {
    if (sourceCrs == targetCrs) { // NOPMD
      // only works when the caching of the CRSs works
      return source;
    }

    CrsTransform crsTransform = getCrsTransform(sourceCrs, targetCrs);
    return transform(source, crsTransform);
  }

  /** @{inheritDoc} */
  public Coordinate transform(Coordinate source, String sourceCrs, String targetCrs) throws GeomajasException {
    if (sourceCrs.equals(targetCrs)) {
      // only works when the caching of the CRSs works
      return source;
    }

    CrsTransform crsTransform = getCrsTransform(sourceCrs, targetCrs);
    return transform(source, crsTransform);
  }

  /** @{inheritDoc} */
  public Coordinate calcDefaultLabelPosition(InternalFeature feature) {
    Geometry geometry = feature.getGeometry();
    Coordinate labelPoint = null;
    if (geometry != null && !geometry.isEmpty() && geometry.isValid()) {
      if (geometry instanceof Polygon || geometry instanceof MultiPolygon) {
        try {
          InteriorPointArea ipa = new InteriorPointArea(geometry);
          labelPoint = ipa.getInteriorPoint();
        } catch (Throwable t) { // NOPMD
          // BUG in JTS for some valid geometries ? fall back to centroid
          log.warn("getInteriorPoint() failed", t);
        }
      } else if (geometry instanceof LineString || geometry instanceof MultiLineString) {
        InteriorPointLine ipa = new InteriorPointLine(geometry);
        labelPoint = ipa.getInteriorPoint();
      } else {
        labelPoint = geometry.getCentroid().getCoordinate();
      }
    }
    if (null == labelPoint && null != geometry) {
      Point centroid = geometry.getCentroid();
      if (null != centroid) {
        labelPoint = centroid.getCoordinate();
      }
    }
    if (null != labelPoint && (Double.isNaN(labelPoint.x) || Double.isNaN(labelPoint.y))) {
      labelPoint = new Coordinate(geometry.getCoordinate());
    }
    return null == labelPoint ? null : new Coordinate(labelPoint);
  }

  /** @{inheritDoc} */
  public Geometry createCircle(final Point point, final double radius, final int nrPoints) {
    double x = point.getX();
    double y = point.getY();
    Coordinate[] coords = new Coordinate[nrPoints + 1];
    for (int i = 0; i < nrPoints; i++) {
      double angle = ((double) i / (double) nrPoints) * Math.PI * 2.0;
      double dx = Math.cos(angle) * radius;
      double dy = Math.sin(angle) * radius;
      coords[i] = new Coordinate(x + dx, y + dy);
    }
    coords[nrPoints] = coords[0];

    LinearRing ring = point.getFactory().createLinearRing(coords);
    return point.getFactory().createPolygon(ring, null);
  }
}
TOP

Related Classes of org.geomajas.internal.service.GeoServiceImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.