All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geotools.data.util.ScreenMap Maven / Gradle / Ivy

/*
 *    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 - 2025 Weber Informatics LLC | Privacy Policy