Create vtm-jts module, closes #53

This commit is contained in:
Emux
2016-07-07 20:16:33 +03:00
parent e74052c164
commit 37ace257cf
21 changed files with 519 additions and 89 deletions

View File

@@ -0,0 +1,149 @@
package org.oscim.layers.vector.geometries;
import org.oscim.core.GeoPoint;
import org.oscim.utils.geom.GeomBuilder;
/**
* Predefined class for drawing circles on the map. Circles are by default
* made of 32 segments.
*/
public class CircleDrawable extends JtsDrawable {
public static int MEDIUM_QUALITY = 32;
public static int HIGH_QUALITY = 64;
/**
* Constructs a circle given the real-world radius in km. Keep in mind that
* this technique is computationally costly for circles with huge number or
* segments.
*
* @param center GeoPoint - center of the circle
* @param radiusKm Radius of the circle in kilometers.
*/
public CircleDrawable(GeoPoint center, double radiusKm) {
super(Style.DEFAULT_STYLE);
GeomBuilder gb = new GeomBuilder();
for (int i = 0; i < MEDIUM_QUALITY; i++) {
GeoPoint point = findGeoPointWithGivenDistance(center,
i * Math.PI / MEDIUM_QUALITY * 2,
radiusKm);
gb.points(point.getLongitude(), point.getLatitude());
}
geometry = gb.toPolygon();
}
/**
* Constructs a circle given the real-world radius in km. Keep in mind that
* this technique is computationally costly for circles with huge number or
* segments.
*
* @param center GeoPoint - center of the circle
* @param radiusKm Radius of the circle in kilometers. The size of the
* circle may be distorted due to the Mercator projections
* properties.
* @param style FillableGeometryStyle with color and transparency
* information for the circle
*/
public CircleDrawable(GeoPoint center, double radiusKm, Style style) {
super(style);
GeomBuilder gb = new GeomBuilder();
for (int i = 0; i < MEDIUM_QUALITY; i++) {
GeoPoint point = findGeoPointWithGivenDistance(center,
i * Math.PI / MEDIUM_QUALITY * 2,
radiusKm);
gb.points(point.getLongitude(), point.getLatitude());
}
geometry = gb.toPolygon();
}
/**
* Constructs a circle given the real-world radius in km. Keep in mind that
* this technique is computationally costly for circles with huge number or
* segments.
*
* @param center GeoPoint - center of the circle
* @param radiusKm Radius of the circle in kilometers. The size of the
* circle may be distorted due to the Mercator projections
* properties.
* @param quadrantSegments the number of segments a quarter of circle will
* have. Use Circle.LOW_PRECISION for quick rendering,
* Circle.MEDIUM_PRECISION for good rendering and quality or
* Circle.HIGH_PRECISION for high quality.
* @param style FillableGeometryStyle with color and transparency
* information for the circle
*/
public CircleDrawable(GeoPoint center, double radiusKm, int quadrantSegments,
Style style) {
super(style);
GeomBuilder gb = new GeomBuilder();
for (int i = 0; i < quadrantSegments; i++) {
GeoPoint point = findGeoPointWithGivenDistance(center,
i * Math.PI / quadrantSegments * 2,
radiusKm);
gb.points(point.getLongitude(), point.getLatitude());
}
geometry = gb.toPolygon();
}
/**
* This function finds a GeoPoint offset by a distance in the direction
* given in the bearing parameter. It is an approximation due to the
* Mercator projections properties
*
* @param startPoint
* @param initialBearingRadians
* @param distanceKilometres
* @return a new GeoPoint located distanceKilometers away from the
* startPoint in the direction of the initialBearing
*/
private static GeoPoint findGeoPointWithGivenDistance(GeoPoint startPoint,
double initialBearingRadians, double distanceKilometres)
{
double radiusEarthKilometres = 6371.01;
double distRatio = distanceKilometres / radiusEarthKilometres;
double distRatioSine = Math.sin(distRatio);
double distRatioCosine = Math.cos(distRatio);
double startLatRad = degreesToRadians(startPoint.getLatitude());
double startLonRad = degreesToRadians(startPoint.getLongitude());
double startLatCos = Math.cos(startLatRad);
double startLatSin = Math.sin(startLatRad);
double endLatRads = Math.asin((startLatSin * distRatioCosine)
+ (startLatCos * distRatioSine * Math.cos(initialBearingRadians)));
double endLonRads = startLonRad
+ Math.atan2(
Math.sin(initialBearingRadians) * distRatioSine * startLatCos,
distRatioCosine - startLatSin * Math.sin(endLatRads));
return new GeoPoint(radiansToDegrees(endLatRads), radiansToDegrees(endLonRads));
}
/**
* translates an angle from degrees to radians
*
* @param degrees
* @return the angle in radians
*/
private static double degreesToRadians(double degrees)
{
double degToRadFactor = Math.PI / 180;
return degrees * degToRadFactor;
}
/**
* translates an angle from radians to degrees
*
* @param radians
* @return the angle in degrees
*/
private static double radiansToDegrees(double radians)
{
double radToDegFactor = 180 / Math.PI;
return radians * radToDegFactor;
}
}

View File

@@ -0,0 +1,16 @@
package org.oscim.layers.vector.geometries;
import com.vividsolutions.jts.geom.Geometry;
public interface Drawable {
/**
* @return
*/
public Style getStyle();
/**
* @return
*/
public Geometry getGeometry();
}

View File

@@ -0,0 +1,88 @@
package org.oscim.layers.vector.geometries;
import org.oscim.core.GeoPoint;
import org.oscim.core.MercatorProjection;
import org.oscim.core.Point;
import org.oscim.utils.geom.GeomBuilder;
/**
* Predefined class for drawing hexagons on the map.
*/
public class HexagonDrawable extends JtsDrawable {
/**
* @param center GeoPoint - center of the hexagon
* @param radiusKm Radius of the hexagon in kilometers. The size of the
* hexagon may be distorted due to the Mercator projections
* properties.
*/
public HexagonDrawable(GeoPoint center, double radiusKm) {
super(Style.DEFAULT_STYLE);
GeomBuilder gb = new GeomBuilder();
for (int i = 0; i < 6; i++) {
GeoPoint point = findGeoPointWithGivenDistance(center, i * Math.PI / 3, radiusKm);
gb.points(point.getLongitude(), point.getLatitude());
}
geometry = gb.toPolygon();
}
/**
* @param center GeoPoint - center of the hexagon
* @param radiusKm Radius of the hexagon in kilometers. The size of the
* hexagon may be distorted due to the Mercator projections
* properties.
* @param rotationRad rotation of the hexagon in radians
* @param style
*/
public HexagonDrawable(GeoPoint center, double radiusKm, double rotationRad, Style style) {
super(style);
GeomBuilder gb = new GeomBuilder();
Point tmp = new Point();
for (int i = 0; i < 6; i++) {
GeoPoint point = findGeoPointWithGivenDistance(center,
rotationRad + i * Math.PI / 3,
radiusKm);
MercatorProjection.project(point, tmp);
gb.points(tmp.x, tmp.y);
}
geometry = gb.toPolygon();
}
/**
* This function finds a GeoPoint offset by a distance in the direction
* given in the bearing parameter. It is an approximation due to the
* Mercator projections properties
*
* @param startPoint
* @param initialBearingRadians
* @param distanceKilometres
* @return a new GeoPoint located distanceKilometers away from the
* startPoint in the direction of the initialBearing
*/
private static GeoPoint findGeoPointWithGivenDistance(GeoPoint startPoint,
double initialBearingRadians, double distanceKilometres)
{
double radiusEarthKilometres = 6371.01;
double distRatio = distanceKilometres / radiusEarthKilometres;
double distRatioSine = Math.sin(distRatio);
double distRatioCosine = Math.cos(distRatio);
double startLatRad = Math.toRadians(startPoint.getLatitude());
double startLonRad = Math.toRadians(startPoint.getLongitude());
double startLatCos = Math.cos(startLatRad);
double startLatSin = Math.sin(startLatRad);
double endLatRads = Math.asin((startLatSin * distRatioCosine)
+ (startLatCos * distRatioSine * Math.cos(initialBearingRadians)));
double endLonRads = startLonRad
+ Math.atan2(
Math.sin(initialBearingRadians) * distRatioSine * startLatCos,
distRatioCosine - startLatSin * Math.sin(endLatRads));
return new GeoPoint(Math.toDegrees(endLatRads), Math.toDegrees(endLonRads));
}
}

View File

@@ -0,0 +1,64 @@
package org.oscim.layers.vector.geometries;
import java.util.List;
import org.oscim.core.GeoPoint;
import org.oscim.utils.geom.GeomBuilder;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.impl.PackedCoordinateSequenceFactory;
public class JtsDrawable implements Drawable {
public static final PackedCoordinateSequenceFactory coordFactory;
public static final GeometryFactory geomFactory;
static {
coordFactory = new PackedCoordinateSequenceFactory();
geomFactory = new GeometryFactory(coordFactory);
}
protected Style style;
protected Geometry geometry;
public JtsDrawable(Style style) {
this.style = style;
}
public JtsDrawable(Geometry geometry, Style style) {
this.geometry = geometry;
this.style = style;
}
/**
* @param style
*/
public void setStyle(Style style) {
this.style = style;
}
/* (non-Javadoc)
*
* @see org.oscim.core.geometries.Drawable#getStyle() */
@Override
public Style getStyle() {
return style;
}
/* (non-Javadoc)
*
* @see org.oscim.core.geometries.Drawable#getGeometry() */
@Override
public Geometry getGeometry() {
return geometry;
}
protected static GeomBuilder loadPoints(GeomBuilder gb, List<GeoPoint> points) {
for (GeoPoint point : points) {
gb.point(point.getLongitude(),
point.getLatitude());
}
return gb;
}
}

View File

@@ -0,0 +1,44 @@
package org.oscim.layers.vector.geometries;
import java.util.List;
import org.oscim.core.GeoPoint;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.LineString;
/**
* Predefined class for drawing lines and line strings on the map.
*/
public class LineDrawable extends JtsDrawable {
public LineDrawable(Geometry line, Style style) {
super(style);
if (line.getDimension() != 1)
throw new IllegalArgumentException("Geometry not a Line");
this.geometry = line;
}
public LineDrawable(List<GeoPoint> points) {
this(points, Style.defaultStyle());
}
public LineDrawable(List<GeoPoint> points, Style style) {
super(style);
if (points.size() < 2)
return;
double[] coords = new double[points.size() * 2];
int c = 0;
for (GeoPoint p : points) {
coords[c++] = p.getLongitude();
coords[c++] = p.getLatitude();
}
this.geometry = new LineString(coordFactory.create(coords, 2), geomFactory);
}
public LineDrawable(double[] lonLat, Style style) {
this(new LineString(coordFactory.create(lonLat, 2), geomFactory), style);
}
}

View File

@@ -0,0 +1,36 @@
package org.oscim.layers.vector.geometries;
import org.oscim.core.GeoPoint;
import org.oscim.utils.geom.GeomBuilder;
/**
* Use this class to draw points and circles which size has to be specified in
* screen units. Points (and circles resulting from this class) do not have
* area,
* however during rendering to make the point visible at varying zoom levels
* a buffer area is built around it.
* To give the point custom size, create a GeometryBuffer with, set buffer to
* your value and assign the point the final style.
*
* Note that since points do not have any area, they are not generalized.
*
* Normally points retain their size in the screen units across all zoom levels
* but this can be customized. Use setStartLevel on the point's style to specify
* from which zoom level the point should "stick to the map" and not decrease in
* size.
*/
public class PointDrawable extends JtsDrawable {
public PointDrawable(GeoPoint point) {
this(point, Style.defaultStyle());
}
public PointDrawable(GeoPoint point, Style style) {
this(point.getLongitude(), point.getLatitude(), style);
}
public PointDrawable(double lat, double lon, Style style) {
super(style);
this.geometry = new GeomBuilder().points(lon, lat).toPoint();
}
}

View File

@@ -0,0 +1,116 @@
package org.oscim.layers.vector.geometries;
import java.util.Arrays;
import java.util.List;
import org.oscim.core.GeoPoint;
import org.oscim.utils.geom.GeomBuilder;
import com.vividsolutions.jts.geom.Geometry;
/**
* Predefined class to draw polygons on the map.
*/
public class PolygonDrawable extends JtsDrawable {
/**
* Creates a polygon using a JTS geometry and a FillableGeometryStyle
*
* @param polygon
* @param style
*/
public PolygonDrawable(Geometry polygon, Style style) {
super(style);
if (polygon.getDimension() != 2)
throw new IllegalArgumentException("Geometry not a Polygon");
this.geometry = polygon;
}
/**
* Creates a polygon using the coordinates provided in the List
*
* @param points
*/
public PolygonDrawable(List<GeoPoint> points) {
this(points, Style.defaultStyle());
}
/**
* Create a polygon given the array of GeoPoints and a FillableGeometryStyle
*
* @param points
* @param style
*/
public PolygonDrawable(Style style, GeoPoint... points) {
this(Arrays.asList(points), style);
}
/**
* @param points
* @param style
*/
public PolygonDrawable(List<GeoPoint> points, Style style) {
this(loadPoints(new GeomBuilder(), points).toPolygon(), style);
}
/**
* Create a polygon given the array of GeoPoints for the boundary, the array
* of GeoPoints for the hole and outline and fill color and alpha
*
* @param points
* @param holePoints
* @param outlineColor
* @param outlineAlpha
* @param fillColor
* @param fillAlpha
*/
public PolygonDrawable(GeoPoint[] points, GeoPoint[] holePoints, float lineWidth,
int lineColor,
int fillColor, float fillAlpha) {
this(Arrays.asList(points),
Arrays.asList(holePoints),
lineWidth, lineColor, fillColor, fillAlpha);
}
/**
* Create a polygon using the Coordinates provided in the first List, with a
* hole build from the Coordinates in the second List and outline and fill -
* color and alpha
*
* @param points
* @param holePoints
* @param outlineColor
* @param outlineAlpha
* @param fillColor
* @param fillAlpha
*/
public PolygonDrawable(List<GeoPoint> points, List<GeoPoint> holePoints,
float lineWidth, int lineColor, int fillColor, float fillAlpha) {
this(points, holePoints, new Style.Builder()
.strokeWidth(lineWidth)
.strokeColor(lineColor)
.fillColor(fillColor)
.fillAlpha(fillAlpha)
.build());
}
/**
* Creates a polygon from a List of coordinates in the first List, with a
* hole from coordinates in the second List and requires a
* FillableGeometryStyle for the color information
*
* @param points
* @param holePoints
* @param style
*/
public PolygonDrawable(List<GeoPoint> points, List<GeoPoint> holePoints, Style style) {
super(style);
GeomBuilder gb = new GeomBuilder();
loadPoints(gb, points).ring();
loadPoints(gb, holePoints).ring();
this.geometry = gb.toPolygon();
this.style = style;
}
}

View File

@@ -0,0 +1,55 @@
package org.oscim.layers.vector.geometries;
import org.oscim.core.GeoPoint;
import org.oscim.utils.geom.GeomBuilder;
/**
* Predefined class to draw rectangles on the map
*/
public class RectangleDrawable extends JtsDrawable {
/**
* Creates a Rectangle given the top-left and the bottom-right coordinate of
* it
*
* @param topLeft
* @param bottomRight
*/
public RectangleDrawable(GeoPoint topLeft, GeoPoint bottomRight) {
this(topLeft, bottomRight, Style.defaultStyle());
}
/**
* Creates a Rectangle given the top-left and the bottom-right coordinate of
* it
*
* @param topLeft
* @param bottomRight
*/
public RectangleDrawable(GeoPoint topLeft, GeoPoint bottomRight, Style style) {
super(style);
geometry = new GeomBuilder()
.point(topLeft.getLongitude(), topLeft.getLatitude())
.point(bottomRight.getLongitude(), topLeft.getLatitude())
.point(bottomRight.getLongitude(), bottomRight.getLatitude())
.point(topLeft.getLongitude(), bottomRight.getLatitude())
.toPolygon();
}
/**
* Creates a Rectangle given the top-left and the bottom-right coordinate of
* it
*
* @param topLeft
* @param bottomRight
*/
public RectangleDrawable(double minLat, double minLon, double maxLat, double maxLon, Style style) {
super(style);
geometry = new GeomBuilder()
.point(minLon, minLat)
.point(minLon, maxLat)
.point(maxLon, maxLat)
.point(maxLon, minLat)
.toPolygon();
}
}

View File

@@ -0,0 +1,198 @@
package org.oscim.layers.vector.geometries;
import org.oscim.backend.canvas.Color;
/**
* Class encapsulating style information for drawing geometries on the map.
*/
public class Style {
public static final int GENERALIZATION_HIGH = 1 << 3;
public static final int GENERALIZATION_MEDIUM = 1 << 2;
public static final int GENERALIZATION_SMALL = 1 << 0;
public static final int GENERALIZATION_NONE = 0;
public final float strokeWidth;
public final int strokeColor;
public final int fillColor;
public final float fillAlpha;
public final double buffer;
public final int scalingZoomLevel;
public final int generalization;
private Style(Builder builder) {
strokeWidth = builder.strokeWidth;
strokeColor = builder.strokeColor;
fillColor = builder.fillColor;
fillAlpha = builder.fillAlpha;
buffer = builder.buffer;
scalingZoomLevel = builder.scalingZoomLevel;
generalization = builder.generalization;
}
/**
* Geometry style builder. Usage example:
*
* <pre>
* {
* Style style = Style.builder()
* .strokeWidth(1f).strokeColor(Color.BLACK).build();
* }
* </pre>
*/
public static class Builder {
private float strokeWidth = 1f;
private int strokeColor = Color.GRAY;
private int fillColor = Color.GRAY;
private float fillAlpha = 0.25f;
private double buffer = 1;
private int scalingZoomLevel = 1;
private int generalization = GENERALIZATION_NONE;
protected Builder() {
}
/**
* Builds the GeometryStyle from the specified parameters.
*
* @return
*/
public Style build() {
return new Style(this);
}
/**
* Sets the line width for the geometry's line or outline.
*
* @param lineWidth
* @return
*/
public Builder strokeWidth(float lineWidth) {
this.strokeWidth = lineWidth;
return this;
}
/**
* Sets the color for the geometry's line or outline.
*
* @param stokeColor
* @return
*/
public Builder strokeColor(int stokeColor) {
this.strokeColor = stokeColor;
return this;
}
/**
* Sets the color for the geometry's area.
*
* @param fillColor
* @return
*/
public Builder fillColor(int fillColor) {
this.fillColor = fillColor;
return this;
}
/**
* Sets alpha channel value for the geometry's area.
*
* @param fillAlpha
* @return
*/
public Builder fillAlpha(double fillAlpha) {
this.fillAlpha = (float) fillAlpha;
return this;
}
/**
* This function has effect only on Points:
* use it to control the size on the circle that
* will be built from a buffer around the point.
*
* @param buffer
* @return itself
*/
public Builder buffer(double buffer) {
this.buffer = buffer;
return this;
}
/**
* This function has effect only on Points:
* use it to specify from which zoom level the point
* should stop decreasing in size and "stick to the map".
*
* @param zoomlvl
*/
public Builder scaleZoomLevel(int zoomlvl) {
this.scalingZoomLevel = zoomlvl;
return this;
}
/**
* Sets generalization factor for the geometry.
* Use predefined GeometryStyle.GENERALIZATION_HIGH,
* GENERALIZATION_MEDIUM or GENERALIZATION_SMALL
*
* @param generalization
* @return
*/
public Builder generalization(int generalization) {
this.generalization = generalization;
return this;
}
}
public float getStrokeWidth() {
return strokeWidth;
}
public int getStrokeColor() {
return strokeColor;
}
public int getFillColor() {
return fillColor;
}
public float getFillAlpha() {
return fillAlpha;
}
public int getGeneralization() {
return generalization;
}
public double getBuffer() {
return buffer;
}
public int getScalingZoomLevel() {
return scalingZoomLevel;
}
static final Style DEFAULT_STYLE = new Builder()
.fillColor(0xcccccccc)
.fillAlpha(1)
.build();
public static Style defaultStyle() {
return DEFAULT_STYLE;
}
public static Style.Builder builder() {
return new Style.Builder();
}
}