Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.geomajas.gwt2.client.gfx.GeometryPath Maven / Gradle / Ivy
/*
* This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
*
* Copyright 2008-2015 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.gwt2.client.gfx;
import java.util.ArrayList;
import java.util.List;
import org.geomajas.geometry.Bbox;
import org.geomajas.geometry.Coordinate;
import org.geomajas.geometry.Geometry;
import org.geomajas.geometry.service.BboxService;
import org.vaadin.gwtgraphics.client.Shape;
import org.vaadin.gwtgraphics.client.VectorObject;
import org.vaadin.gwtgraphics.client.shape.Path;
import org.vaadin.gwtgraphics.client.shape.path.ClosePath;
import org.vaadin.gwtgraphics.client.shape.path.LineTo;
import org.vaadin.gwtgraphics.client.shape.path.MoveTo;
import org.vaadin.gwtgraphics.client.shape.path.PathStep;
import org.vaadin.gwtgraphics.client.shape.path.ScaleHelper;
/**
* Path based on a coordinate array. Can be used to represent a geometry.
*
* @author Jan De Moerloose
*/
public class GeometryPath extends Shape {
private List steps = new ArrayList();
private boolean closed;
private Coordinate[] coordinates;
private List subPaths = new ArrayList();
private Bbox bounds;
private boolean skipTransform;
/**
* Create a zero-length path at (0,0).
*
* @param closed if true, the path is closed or a ring, else the path is open or a line.
*/
public GeometryPath(boolean closed) {
this(new Coordinate[] { new Coordinate(), new Coordinate() }, closed);
}
/**
* Create a path with these coordinates.
*
* @param coordinates
* @param closed if true, path is closed
*/
public GeometryPath(Coordinate[] coordinates, boolean closed) {
super(0, 0);
setClosed(closed);
setCoordinates(coordinates);
}
/**
* Create a path with this Geometry.
*
* @param geometry
*/
public GeometryPath(Geometry geometry) {
super(0, 0);
setGeometry(geometry);
}
public void setGeometry(Geometry geometry) {
setClosed(
Geometry.POLYGON.equals(geometry.getGeometryType())
|| Geometry.MULTI_POLYGON.equals(geometry.getGeometryType())
|| Geometry.LINEAR_RING.equals(geometry.getGeometryType()));
skipTransform = true;
try {
if (Geometry.LINE_STRING.equals(geometry.getGeometryType())) {
setLineString(geometry);
} else if (Geometry.LINEAR_RING.equals(geometry.getGeometryType())) {
setLinearRing(geometry);
} else if (Geometry.POLYGON.equals(geometry.getGeometryType())) {
setPolygon(geometry);
} else if (Geometry.MULTI_LINE_STRING.equals(geometry.getGeometryType())) {
setMultiLineString(geometry);
} else if (Geometry.MULTI_POLYGON.equals(geometry.getGeometryType())) {
setMultiPolygon(geometry);
} else {
throw new IllegalArgumentException("Unsupported geometry " + geometry.getGeometryType());
}
} finally {
skipTransform = false;
drawTransformed();
}
}
@Override
protected Class extends VectorObject> getType() {
return Path.class;
}
public void setCoordinates(Coordinate[] coordinates) {
this.coordinates = coordinates;
updateCoordinates();
}
public Coordinate[] getCoordinates() {
return coordinates;
}
public Coordinate getLastCoordinate() {
return (Coordinate) coordinates[coordinates.length - 1].clone();
}
/**
* Add a new sub-path to the path. A path can have multiple sub-paths. Sub-paths may be disconnected lines or rings
* but, thanks to even-odd rule, holes can be added as well.
*
* @param coordinates
* @param closed
*/
public void addSubPath(Coordinate[] coordinates, boolean closed) {
subPaths.add(new SubPath(coordinates, closed));
updateCoordinates();
}
/**
* Add a coordinate to the path.
*
* @param coordinate
*/
public void addCoordinate(Coordinate coordinate) {
Coordinate[] newCoords = new Coordinate[coordinates.length + 1];
System.arraycopy(coordinates, 0, newCoords, 0, coordinates.length);
newCoords[coordinates.length] = coordinate;
setCoordinates(newCoords);
}
/**
* Move the coordinate at the specified index.
*
* @param coordinate the new coordinate
* @param index
*/
public void moveCoordinate(Coordinate coordinate, int index) {
if (index < coordinates.length) {
coordinates[index] = (Coordinate) coordinate.clone();
}
setCoordinates(coordinates);
}
public int getCoordinateCount() {
return coordinates.length;
}
public void setUserPosition(Coordinate position) {
double x = coordinates[0].getX();
double y = coordinates[0].getY();
double dX = position.getX() - x;
double dY = position.getY() - y;
translateCoordinates(coordinates, dX, dY);
for (SubPath s : subPaths) {
translateCoordinates(s.getCoordinates(), dX, dY);
}
setCoordinates(coordinates);
super.setUserX(x);
super.setUserY(y);
}
@Override
public void setUserX(double userX) {
// called by Shape constructor !
if (coordinates != null) {
setUserPosition(new Coordinate(userX, getUserPosition().getY()));
}
}
@Override
public void setUserY(double userY) {
// called by Shape constructor !
if (coordinates != null) {
setUserPosition(new Coordinate(getUserPosition().getX(), userY));
}
}
public Coordinate getUserPosition() {
return (Coordinate) coordinates[0].clone();
}
public void setUserBounds(Bbox bounds) {
double width = this.bounds.getWidth();
double height = this.bounds.getHeight();
double newWidth = bounds.getWidth();
double newHeight = bounds.getHeight();
double scaleX = width == 0 ? 1 : newWidth / width;
double scaleY = height == 0 ? 1 : newHeight / height;
double x = this.bounds.getX();
double y = this.bounds.getY();
double newX = bounds.getX();
double newY = bounds.getY();
scaleCoordinates(coordinates, scaleX, scaleY, x, y, newX, newY);
for (SubPath s : subPaths) {
scaleCoordinates(s.getCoordinates(), scaleX, scaleY, x, y, newX, newY);
}
updateCoordinates();
}
public Bbox getUserbounds() {
return this.bounds;
}
public Bbox getBounds() {
// transform all points and calculate new bounds
double x1 = getUserbounds().getX() * getScaleX() + getDeltaX();
double y1 = getUserbounds().getY() * getScaleY() + getDeltaY();
double x2 = (getUserX() + getUserbounds().getWidth()) * getScaleX() + getDeltaX();
double y2 = (getUserY() + getUserbounds().getHeight()) * getScaleY() + getDeltaY();
return new Bbox(Math.round(Math.min(x1, x2)), Math.round(Math.min(y1, y2)), Math.abs(x1 - x2),
Math.abs(y1 - y2));
}
public boolean isClosed() {
return closed;
}
private void updateCoordinates() {
this.bounds = calcBounds();
this.steps = calcSteps();
if (!skipTransform) {
drawTransformed();
}
}
protected List calcSteps() {
List result = calcSteps(coordinates, closed);
for (SubPath path : subPaths) {
result.addAll(calcSteps(path.getCoordinates(), path.isClosed()));
}
return result;
}
private List calcSteps(Coordinate[] coordinates, boolean closed) {
List result = new ArrayList();
Coordinate prev = coordinates[0];
result.add(new MoveTo(false, prev.getX(), prev.getY()));
for (int i = 1; i < coordinates.length; i++) {
Coordinate next = coordinates[i];
result.add(new LineTo(true, next.getX() - prev.getX(), next.getY() - prev.getY()));
prev = next;
}
if (closed) {
result.add(new ClosePath());
}
return result;
}
protected Bbox calcBounds() {
Bbox result = calcBounds(coordinates);
for (SubPath s : subPaths) {
result = BboxService.union(result, calcBounds(s.getCoordinates()));
}
return result;
}
private Bbox calcBounds(Coordinate[] coordinates) {
Coordinate c = coordinates[0];
Bbox result = new Bbox(c.getX(), c.getY(), 0, 0);
for (int i = 1; i < coordinates.length; i++) {
c = coordinates[i];
result = BboxService.union(result, new Bbox(c.getX(), c.getY(), 0, 0));
}
return result;
}
protected void drawTransformed() {
if (steps != null) {
// apply translation
MoveTo moveTo = (MoveTo) steps.get(0);
steps.set(0, new MoveTo(moveTo.isRelativeCoords(), moveTo.getUserX() + getDeltaX(), moveTo.getUserY()
+ getDeltaY()));
// apply scale
ScaleHelper scaleHelper = new ScaleHelper(getScaleX(), getScaleY());
for (PathStep step : steps) {
step.scale(scaleHelper);
}
getImpl().drawPath(getElement(), steps);
}
}
private void scaleCoordinates(Coordinate[] coordinates, double scaleX, double scaleY, double x, double y,
double newX, double newY) {
for (int i = 0; i < coordinates.length; i++) {
coordinates[i].setX(newX + scaleX * (coordinates[i].getX() - x));
coordinates[i].setY(newY + scaleY * (coordinates[i].getY() - y));
}
}
private void translateCoordinates(Coordinate[] coordinates, double dX, double dY) {
for (int i = 0; i < coordinates.length; i++) {
coordinates[i].setX(coordinates[i].getX() + dX);
coordinates[i].setY(coordinates[i].getY() + dY);
}
}
private void setMultiPolygon(Geometry multiPolygon) {
if (multiPolygon.getGeometries() != null && multiPolygon.getGeometries().length > 0) {
setPolygon(multiPolygon.getGeometries()[0]);
for (int i = 1; i < multiPolygon.getGeometries().length; i++) {
Geometry polygon = multiPolygon.getGeometries()[i];
for (int j = 0; j < polygon.getGeometries().length; j++) {
Geometry ring = polygon.getGeometries()[j];
Coordinate[] pathCoords = removeLastCoordinate(ring);
addSubPath(pathCoords, true);
}
}
}
}
private void setMultiLineString(Geometry multiLineString) {
if (multiLineString.getGeometries() != null && multiLineString.getGeometries().length > 0) {
setLineString(multiLineString.getGeometries()[0]);
for (int i = 1; i < multiLineString.getGeometries().length; i++) {
Geometry lineString = multiLineString.getGeometries()[i];
addSubPath(clone(lineString.getCoordinates()), false);
}
}
}
private void setPolygon(Geometry polygon) {
if (polygon.getGeometries() != null && polygon.getGeometries().length > 0) {
setLinearRing(polygon.getGeometries()[0]);
getElement().getStyle().setProperty("fillRule", "evenOdd");
for (int i = 1; i < polygon.getGeometries().length; i++) {
Geometry ring = polygon.getGeometries()[i];
Coordinate[] pathCoords = removeLastCoordinate(ring);
if (pathCoords != null && pathCoords.length > 0) {
addSubPath(pathCoords, true);
}
}
}
}
private void setLinearRing(Geometry linearRing) {
Coordinate[] coordinates = linearRing.getCoordinates();
if (coordinates != null && coordinates.length > 1) {
Coordinate[] pathCoords = removeLastCoordinate(linearRing);
setClosed(true);
setCoordinates(pathCoords);
}
}
private void setLineString(Geometry lineString) {
if (lineString.getCoordinates() != null && lineString.getCoordinates().length > 0) {
setCoordinates(clone(lineString.getCoordinates()));
}
}
private Coordinate[] clone(Coordinate[] coordinates) {
Coordinate[] copy = new Coordinate[coordinates.length];
System.arraycopy(coordinates, 0, copy, 0, coordinates.length);
return copy;
}
private Coordinate[] removeLastCoordinate(Geometry ring) {
if (ring.getCoordinates() == null) {
return new Coordinate[0];
}
Coordinate[] coordinates = ring.getCoordinates();
Coordinate[] pathCoords = new Coordinate[coordinates.length - 1];
System.arraycopy(coordinates, 0, pathCoords, 0, coordinates.length - 1);
return pathCoords;
}
private void setClosed(boolean closed) {
this.closed = closed;
if (!closed) {
setFillOpacity(0);
}
}
/**
* Inner class for tracking sub-paths.
*
* @author Jan De Moerloose
*/
class SubPath {
private Coordinate[] coordinates;
private boolean closed;
public SubPath(Coordinate[] coordinates, boolean closed) {
this.coordinates = coordinates;
this.closed = closed;
}
public Coordinate[] getCoordinates() {
return coordinates;
}
public boolean isClosed() {
return closed;
}
}
}