org.geotools.data.util.ScreenMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gt-main Show documentation
Show all versions of gt-main Show documentation
The main module contains the GeoTools public interfaces that are used by
other GeoTools modules (and GeoTools applications). Where possible we make
use industry standard terms as provided by OGC and ISO standards.
The formal GeoTools public api consists of gt-metadata, jts and the gt-main module.
The main module contains the default implementations that are available provided
to other GeoTools modules using our factory system. Factories are obtained from
an appropriate FactoryFinder, giving applications a chance configure the factory
used using the Factory Hints facilities.
FilterFactory ff = CommonFactoryFinder.getFilterFactory();
Expression expr = ff.add( expression1, expression2 );
If you find yourself using implementation specific classes chances are you doing it wrong:
Expression expr = new AddImpl( expression1, expressiom2 );
The newest version!
/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.data.util;
import org.geotools.geometry.jts.JTS;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.CoordinateSequenceFactory;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.MultiLineString;
import org.locationtech.jts.geom.MultiPoint;
import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
/**
* The screenmap is a packed bitmap of the screen, one bit per pixels. It can be used to avoid
* rendering a lot of very small features in the same pixel.
*
* The screenmap can be used two ways:
*
*
* - By working directly against the pixels using {@link #checkAndSet(int, int)}
*
- By working with real world envelopes using {@link #checkAndSet(Envelope)}, in that case the
* full math transform from data to screen, and the generalization spans must be set
*
*
* When checkAndSet returns false the geometry sits in a pixel that has been already populated and
* can be skipped.
*
* @author jeichar
* @author Andrea Aime - OpenGeo
*/
public class ScreenMap {
double[] point = new double[2];
int width;
int height;
private int minx;
private int miny;
MathTransform mt;
double spanX;
double spanY;
BitFieldMatrix bitfield;
public ScreenMap(int x, int y, int width, int height, MathTransform mt) {
this.width = width;
this.height = height;
this.minx = x;
this.miny = y;
this.mt = mt;
}
/** Returns the bitfield, azyly instantiating it as needed */
private BitFieldMatrix getBitField() {
if (this.bitfield == null) {
this.bitfield = new BitFieldMatrix();
}
return bitfield;
}
public ScreenMap(ScreenMap original, int expandBy) {
this(
original.minx - expandBy,
original.miny - expandBy,
original.width + expandBy * 2,
original.height + expandBy * 2);
}
public ScreenMap(int x, int y, int width, int height) {
this(x, y, width, height, null);
}
public void setTransform(MathTransform mt) {
this.mt = mt;
}
public boolean checkAndSet(Envelope envelope) throws TransformException {
if (!canSimplify(envelope)) {
return false;
}
point[0] = (envelope.getMinX() + envelope.getMaxX()) / 2;
point[1] = (envelope.getMinY() + envelope.getMaxY()) / 2;
mt.transform(point, 0, point, 0, 1);
int r = (int) point[0];
int c = (int) point[1];
return checkAndSet(r, c);
}
public boolean canSimplify(Envelope envelope) {
return envelope.getWidth() < spanX && envelope.getHeight() < spanY;
}
public void setSpans(double spanX, double spanY) {
this.spanX = spanX;
this.spanY = spanY;
}
/**
* Checks if the geometry should be skipped. If the test returns true it means the geometry sits
* in a pixel that has already been used
*/
public boolean checkAndSet(int x, int y) {
return getBitField().checkAndSet(x, y);
}
public boolean get(Envelope envelope) throws TransformException {
if (!canSimplify(envelope)) {
return false;
}
point[0] = (envelope.getMinX() + envelope.getMaxX()) / 2;
point[1] = (envelope.getMinY() + envelope.getMaxY()) / 2;
mt.transform(point, 0, point, 0, 1);
int r = (int) point[0];
int c = (int) point[1];
return get(r, c);
}
/** Returns true if the pixel at location x,y is set or out of bounds. */
public boolean get(int x, int y) {
return getBitField().get(x, y);
}
/**
* Returns geometry suitable for rendering the pixel that has just been occupied. The geometry
* is designed to actually fill the pixel
*/
public Geometry getSimplifiedShape(Geometry geometry) {
Envelope envelope = geometry.getEnvelopeInternal();
return getSimplifiedShape(
envelope.getMinX(),
envelope.getMinY(),
envelope.getMaxX(),
envelope.getMaxY(),
geometry.getFactory(),
geometry.getClass());
}
/**
* Returns geometry suitable for rendering the pixel that has just been occupied. The geometry
* is designed to actually fill the pixel
*/
public Geometry getSimplifiedShape(
double minx,
double miny,
double maxx,
double maxy,
GeometryFactory geometryFactory,
Class geometryType) {
CoordinateSequenceFactory csf = geometryFactory.getCoordinateSequenceFactory();
double midx = (minx + maxx) / 2;
double midy = (miny + maxy) / 2;
double x0 = midx - spanX / 2;
double x1 = midx + spanX / 2;
double y0 = midy - spanY / 2;
double y1 = midy + spanY / 2;
if (Point.class.isAssignableFrom(geometryType)
|| MultiPoint.class.isAssignableFrom(geometryType)) {
CoordinateSequence cs = JTS.createCS(csf, 1, 2);
cs.setOrdinate(0, 0, midx);
cs.setOrdinate(0, 1, midy);
if (Point.class.isAssignableFrom(geometryType)) {
// people should not call this method for a point, but... whatever
return geometryFactory.createPoint(cs);
} else {
return geometryFactory.createMultiPoint(
new Point[] {geometryFactory.createPoint(cs)});
}
} else if (LineString.class.isAssignableFrom(geometryType)
|| MultiLineString.class.isAssignableFrom(geometryType)) {
CoordinateSequence cs = JTS.createCS(csf, 2, 2);
cs.setOrdinate(0, 0, x0);
cs.setOrdinate(0, 1, y0);
cs.setOrdinate(1, 0, x1);
cs.setOrdinate(1, 1, y1);
if (MultiLineString.class.isAssignableFrom(geometryType)) {
return geometryFactory.createMultiLineString(
new LineString[] {geometryFactory.createLineString(cs)});
} else {
return geometryFactory.createLineString(cs);
}
} else {
CoordinateSequence cs = JTS.createCS(csf, 5, 2);
cs.setOrdinate(0, 0, x0);
cs.setOrdinate(0, 1, y0);
cs.setOrdinate(1, 0, x0);
cs.setOrdinate(1, 1, y1);
cs.setOrdinate(2, 0, x1);
cs.setOrdinate(2, 1, y1);
cs.setOrdinate(3, 0, x1);
cs.setOrdinate(3, 1, y0);
cs.setOrdinate(4, 0, x0);
cs.setOrdinate(4, 1, y0);
LinearRing ring = geometryFactory.createLinearRing(cs);
if (MultiPolygon.class.isAssignableFrom(geometryType)) {
return geometryFactory.createMultiPolygon(
new Polygon[] {geometryFactory.createPolygon(ring, null)});
} else {
return geometryFactory.createPolygon(ring, null);
}
}
}
/** Sets location at position x,y to the value. */
public void set(int x, int y, boolean value) {
getBitField().set(x, y, value);
}
/**
* Incapsulates the bitfield representation and access logic, allows for lazy creation of the
* bitfield at the first time we actually need to use it (only fairly zoomed in requestes not
* pixel might ever be set)
*/
final class BitFieldMatrix {
int[] pixels;
public BitFieldMatrix() {
int arraySize = ((width * height) / 32) + 1;
pixels = new int[arraySize];
}
public boolean checkAndSet(int x, int y) {
// if it's outside of the screenmap we cannot say whether it's busy or not, and
// we cannot skip it because rendering or geometry transformation might put the geometry
// right in the map
if ((x - minx) < 0
|| (x - minx) > width - 1
|| (y - miny) < 0
|| (y - miny) > height - 1) return false;
int bit = bit(x - minx, y - miny);
int index = bit / 32;
int offset = bit % 32;
int mask = 1 << offset;
try {
if ((pixels[index] & mask) != 0) {
return true;
} else {
pixels[index] = pixels[index] | mask;
return false;
}
} catch (Exception e) {
return true;
}
}
public boolean get(int x, int y) {
// if it's outside of the screenmap we cannot say whether it's busy or not, and
// we cannot skip it because rendering or geometry transformation might put the geometry
// right in the map
if ((x - minx) < 0
|| (x - minx) > width - 1
|| (y - miny) < 0
|| (y - miny) > height - 1) return false;
int bit = bit(x - minx, y - miny);
int index = bit / 32;
int offset = bit % 32;
int mask = 1 << offset;
try {
return ((pixels[index] & mask) != 0) ? true : false;
} catch (Exception e) {
return true;
}
}
public void set(int x, int y, boolean value) {
if ((x - minx) < 0
|| (x - minx) > width - 1
|| (y - miny) < 0
|| (y - miny) > height - 1) return;
int bit = bit(x - minx, y - miny);
int index = bit / 32;
int offset = bit % 32;
int mask = 1;
mask = mask << offset;
if (value) {
pixels[index] = pixels[index] | mask;
} else {
int tmp = pixels[index];
tmp = ~tmp;
tmp = (tmp | mask);
tmp = ~tmp;
pixels[index] = tmp;
}
}
private int bit(int x, int y) {
return (width * y) + x;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy