/* * This file is part of JGrasstools (http://www.jgrasstools.org) * (C) HydroloGIS - www.hydrologis.com * * JGrasstools is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.jgrasstools.nww.layers.defaults.spatialite; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; import org.geotools.geometry.jts.JTS; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.jgrasstools.dbs.compat.ASpatialDb; import org.jgrasstools.dbs.spatialite.SpatialiteGeometryColumns; import org.jgrasstools.gears.io.las.spatialite.LasCell; import org.jgrasstools.gears.io.las.spatialite.LasCellsTable; import org.jgrasstools.gears.io.las.spatialite.LasLevel; import org.jgrasstools.gears.io.las.spatialite.LasLevelsTable; import org.jgrasstools.gears.io.las.spatialite.LasSource; import org.jgrasstools.gears.io.las.spatialite.LasSourcesTable; import org.jgrasstools.gears.utils.CrsUtilities; import org.jgrasstools.gears.utils.TransformationUtils; import org.jgrasstools.gears.utils.colors.ColorInterpolator; import org.jgrasstools.gears.utils.colors.EColorTables; import org.jgrasstools.gears.utils.geometry.GeometryUtilities; import org.jgrasstools.nww.layers.defaults.NwwLayer; import org.jgrasstools.nww.layers.defaults.raster.BasicMercatorTiledImageLayer; import org.jgrasstools.nww.utils.NwwUtilities; import org.jgrasstools.nww.utils.cache.CacheUtils; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import com.vividsolutions.jts.awt.PointTransformation; import com.vividsolutions.jts.awt.ShapeWriter; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.prep.PreparedGeometry; import com.vividsolutions.jts.geom.prep.PreparedGeometryFactory; import com.vividsolutions.jts.operation.union.CascadedPolygonUnion; import gov.nasa.worldwind.avlist.AVKey; import gov.nasa.worldwind.avlist.AVList; import gov.nasa.worldwind.avlist.AVListImpl; import gov.nasa.worldwind.geom.Angle; import gov.nasa.worldwind.geom.LatLon; import gov.nasa.worldwind.geom.Sector; import gov.nasa.worldwind.layers.mercator.MercatorSector; import gov.nasa.worldwind.util.LevelSet; import gov.nasa.worldwind.util.Tile; import gov.nasa.worldwind.util.TileUrlBuilder; /** * Procedural layer for spatialite las tables folder. * * @author Andrea Antonello (www.hydrologis.com) */ public class RasterizedSpatialiteLasLayer extends BasicMercatorTiledImageLayer implements NwwLayer { private static final String ELEVATION = "elevation"; private static final String INTENSITY = "intensity"; private String layerName = "unknown layer"; private static final int TILESIZE = 512; private Coordinate centre; public RasterizedSpatialiteLasLayer( String title, ASpatialDb db, Integer tileSize, boolean transparentBackground, boolean doIntensity ) throws Exception { super(makeLevels(title, tileSize, transparentBackground, db, doIntensity)); String plus = doIntensity ? INTENSITY : ELEVATION; this.layerName = title + " " + plus; this.setUseTransparentTextures(true); try { Envelope tableBounds = db.getTableBounds(LasSourcesTable.TABLENAME); SpatialiteGeometryColumns spatialiteGeometryColumns = db.getGeometryColumnsForTable(LasCellsTable.TABLENAME); CoordinateReferenceSystem dataCrs = CRS.decode("EPSG:" + spatialiteGeometryColumns.srid); CoordinateReferenceSystem targetCRS = DefaultGeographicCRS.WGS84; ReferencedEnvelope env = new ReferencedEnvelope(tableBounds, dataCrs); ReferencedEnvelope envLL = env.transform(targetCRS, true); centre = envLL.centre(); } catch (Exception e) { e.printStackTrace(); centre = CrsUtilities.WORLD.centre(); } } public static boolean isLasDb( ASpatialDb db ) throws Exception { return LasSourcesTable.isLasDatabase(db); } private static LevelSet makeLevels( String title, Integer tileSize, boolean transparentBackground, ASpatialDb db, boolean doIntensity ) throws Exception { String plus = doIntensity ? INTENSITY : ELEVATION; String cacheRelativePath = "rasterized_spatialites/" + title + "_" + plus + "-tiles"; File cacheRoot = CacheUtils.getCacheRoot(); final File cacheFolder = new File(cacheRoot, cacheRelativePath); if (!cacheFolder.exists()) { cacheFolder.mkdirs(); } CacheUtils.clearCacheBySourceName(cacheRelativePath); AVList params = new AVListImpl(); if (tileSize == null || tileSize < 256) { tileSize = TILESIZE; } int finalTileSize = tileSize; SpatialiteGeometryColumns spatialiteGeometryColumns = db.getGeometryColumnsForTable(LasCellsTable.TABLENAME); CoordinateReferenceSystem dataCrs = CRS.decode("EPSG:" + spatialiteGeometryColumns.srid); CoordinateReferenceSystem nwwCRS = DefaultGeographicCRS.WGS84; List<LasSource> lasSources = LasSourcesTable.getLasSources(db); double min = Double.POSITIVE_INFINITY; double max = Double.NEGATIVE_INFINITY; List<Polygon> polList = new ArrayList<>(); ColorInterpolator colorInterp; int lasLevels = 0; if (!doIntensity) { for( LasSource lasSource : lasSources ) { lasLevels = lasSource.levels; polList.add(lasSource.polygon); min = Math.min(min, lasSource.minElev); max = Math.max(max, lasSource.maxElev); } colorInterp = new ColorInterpolator(EColorTables.elev.name(), min, max, null); } else { for( LasSource lasSource : lasSources ) { lasLevels = lasSource.levels; polList.add(lasSource.polygon); min = Math.min(min, lasSource.minIntens); max = Math.max(max, lasSource.maxIntens); } colorInterp = new ColorInterpolator(EColorTables.rainbow.name(), 0, 255, null); } final int _lasLevels = lasLevels; Geometry sourcesUnionData = CascadedPolygonUnion.union(polList); PreparedGeometry preparedCoverage = PreparedGeometryFactory.prepare(sourcesUnionData); MathTransform nww2DataTransform = CRS.findMathTransform(nwwCRS, dataCrs); MathTransform data2NwwTransform = CRS.findMathTransform(dataCrs, nwwCRS); // String urlString = folderFile.toURI().toURL().toExternalForm(); // params.setValue(AVKey.URL, urlString); params.setValue(AVKey.TILE_WIDTH, finalTileSize); params.setValue(AVKey.TILE_HEIGHT, finalTileSize); params.setValue(AVKey.DATA_CACHE_NAME, cacheRelativePath); params.setValue(AVKey.SERVICE, "*"); params.setValue(AVKey.DATASET_NAME, "*"); params.setValue(AVKey.FORMAT_SUFFIX, ".png"); params.setValue(AVKey.NUM_LEVELS, 22); params.setValue(AVKey.NUM_EMPTY_LEVELS, 8); params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, new LatLon(Angle.fromDegrees(22.5d), Angle.fromDegrees(45d))); params.setValue(AVKey.SECTOR, new MercatorSector(-1.0, 1.0, Angle.NEG180, Angle.POS180)); params.setValue(AVKey.TILE_URL_BUILDER, new TileUrlBuilder(){ public URL getURL( Tile tile, String altImageFormat ) throws MalformedURLException { try { int zoom = tile.getLevelNumber() + 3; Sector sector = tile.getSector(); double north = sector.getMaxLatitude().degrees; double south = sector.getMinLatitude().degrees; double east = sector.getMaxLongitude().degrees; double west = sector.getMinLongitude().degrees; double centerX = west + (east - west) / 2.0; double centerY = south + (north - south) / 2.0; int[] tileNumber = NwwUtilities.getTileNumber(centerY, centerX, zoom); int x = tileNumber[0]; int y = tileNumber[1]; Rectangle imageBounds = new Rectangle(0, 0, finalTileSize, finalTileSize); ReferencedEnvelope tileEnvNww = new ReferencedEnvelope(west, east, south, north, DefaultGeographicCRS.WGS84); AffineTransform worldToPixel = TransformationUtils.getWorldToPixel(tileEnvNww, imageBounds); PointTransformation pointTransformation = new PointTransformation(){ @Override public void transform( Coordinate src, Point2D dest ) { worldToPixel.transform(new Point2D.Double(src.x, src.y), dest); } }; Polygon polygonNww = GeometryUtilities.createPolygonFromEnvelope(tileEnvNww); Geometry polygonData = JTS.transform(polygonNww, nww2DataTransform); int imgType; Color backgroundColor; boolean intersects = preparedCoverage.intersects(polygonData); if (transparentBackground || !intersects) { imgType = BufferedImage.TYPE_INT_ARGB; backgroundColor = new Color(Color.WHITE.getRed(), Color.WHITE.getGreen(), Color.WHITE.getBlue(), 0); } else { imgType = BufferedImage.TYPE_INT_RGB; backgroundColor = Color.WHITE; } BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, imgType); Graphics2D gr = image.createGraphics(); gr.setPaint(backgroundColor); gr.fill(imageBounds); // gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, // RenderingHints.VALUE_ANTIALIAS_ON); if (zoom < 12) { Geometry sourcesUnionNww = JTS.transform(sourcesUnionData, data2NwwTransform); drawSources(db, pointTransformation, sourcesUnionNww, gr, finalTileSize); } else if (zoom < 14) { drawLevels(db, colorInterp, pointTransformation, polygonData, gr, _lasLevels, data2NwwTransform, doIntensity, finalTileSize); } else if (zoom < 15 && _lasLevels - 1 > 0) { drawLevels(db, colorInterp, pointTransformation, polygonData, gr, _lasLevels - 1, data2NwwTransform, doIntensity, finalTileSize); } else if (zoom < 17 && _lasLevels - 2 > 0) { drawLevels(db, colorInterp, pointTransformation, polygonData, gr, _lasLevels - 2, data2NwwTransform, doIntensity, finalTileSize); } else if (zoom > 18) { drawPoints(db, colorInterp, pointTransformation, polygonData, gr, data2NwwTransform, doIntensity, finalTileSize); } else { drawCells(db, colorInterp, pointTransformation, polygonData, gr, data2NwwTransform, doIntensity, finalTileSize); } File tileImageFolderFile = new File(cacheFolder, zoom + File.separator + x); if (!tileImageFolderFile.exists()) { tileImageFolderFile.mkdirs(); } File imgFile = new File(tileImageFolderFile, y + ".png"); if (!imgFile.exists()) { ImageIO.write(image, "png", imgFile); } return imgFile.toURI().toURL(); } catch (Exception e) { e.printStackTrace(); return null; } } }); return new LevelSet(params); } private static void drawCells( ASpatialDb db, ColorInterpolator colorInterp, PointTransformation pointTransformation, Geometry polygon, Graphics2D gr, MathTransform data2NwwTransform, boolean doIntensity, int finalTileSize ) throws Exception { int maxPerImage = 100000; List<LasCell> lasCells = LasCellsTable.getLasCells(db, null, polygon, true, true, false, false, false, maxPerImage); int size = lasCells.size(); if (size > 0) { int jump = size / maxPerImage; for( int i = 0; i < size; i = i + 1 + jump ) { LasCell lasCell = lasCells.get(i); if (lasCell.pointsCount == 0) { continue; } Polygon levelPolygon = lasCell.polygon; Geometry polygonNww = JTS.transform(levelPolygon, data2NwwTransform); GeneralPath p = polygonToPath(pointTransformation, polygonNww, finalTileSize); Color c = colorInterp.getColorFor(doIntensity ? lasCell.avgIntensity : lasCell.avgElev); gr.setPaint(c); gr.fill(p); } } } private static void drawPoints( ASpatialDb db, ColorInterpolator colorInterp, PointTransformation pointTransformation, Geometry polygon, Graphics2D gr, MathTransform data2NwwTransform, boolean doIntensity, int finalTileSize ) throws Exception { int maxPerImage = 100000; List<LasCell> lasCells = LasCellsTable.getLasCells(db, null, polygon, true, true, false, false, false, maxPerImage); int size = lasCells.size(); int pointSize = 4; int pointSizehalf = 2; if (size > 0) { int jump = size / maxPerImage; if (jump > 0) { System.err.println("Jump: " + jump); } final Point2D newP = new Point2D.Double(); final Coordinate outCoord = new Coordinate(); if (!doIntensity) { for( int i = 0; i < size; i = i + 1 + jump ) { LasCell lasCell = lasCells.get(i); double[][] cellPositions = LasCellsTable.getCellPositions(lasCell); if (cellPositions != null) for( double[] position : cellPositions ) { Coordinate coord = new Coordinate(position[0], position[1]); Color c = colorInterp.getColorFor(coord.z); JTS.transform(coord, outCoord, data2NwwTransform); pointTransformation.transform(outCoord, newP); gr.setPaint(c); double x = newP.getX(); double y = newP.getY(); if (x < 0) { continue; } if (y < 0) { continue; } if (x > TILESIZE) { continue; } if (y > TILESIZE) { continue; } gr.fillOval((int) x - pointSizehalf, (int) y - pointSizehalf, pointSize, pointSize); } } } else { for( int i = 0; i < size; i = i + 1 + jump ) { LasCell lasCell = lasCells.get(i); double[][] cellPositions = LasCellsTable.getCellPositions(lasCell); short[][] cellInt = LasCellsTable.getCellIntensityClass(lasCell); if (cellPositions != null && cellInt != null) for( int j = 0; j < cellPositions.length; j++ ) { Coordinate coord = new Coordinate(cellPositions[j][0], cellPositions[j][1]); Color c = colorInterp.getColorFor(cellInt[j][0]); JTS.transform(coord, outCoord, data2NwwTransform); pointTransformation.transform(outCoord, newP); gr.setPaint(c); double x = newP.getX(); double y = newP.getY(); if (x < 0) { continue; } if (y < 0) { continue; } if (x > TILESIZE) { continue; } if (y > TILESIZE) { continue; } gr.fillOval((int) x - pointSizehalf, (int) y - pointSizehalf, pointSize, pointSize); } } } } } private static void drawLevels( ASpatialDb db, ColorInterpolator colorInterp, PointTransformation pointTransformation, Geometry polygon, Graphics2D gr, int lasLevelsNum, MathTransform data2NwwTransform, boolean doIntensity, int finalTileSize ) throws Exception { int maxPerImage = 100000; List<LasLevel> lasLevels = LasLevelsTable.getLasLevels(db, lasLevelsNum, polygon); int size = lasLevels.size(); if (size > 0) { int jump = size / maxPerImage; for( int i = 0; i < size; i = i + 1 + jump ) { LasLevel lasLevel = lasLevels.get(i); Polygon levelPolygon = lasLevel.polygon; Geometry polygonNww = JTS.transform(levelPolygon, data2NwwTransform); GeneralPath p = polygonToPath(pointTransformation, polygonNww, finalTileSize); Color c = colorInterp.getColorFor(doIntensity ? lasLevel.avgIntensity : lasLevel.avgElev); gr.setPaint(c); gr.fill(p); } } } private static GeneralPath polygonToPath( PointTransformation pointTransformation, Geometry polygon, int finalTileSize ) { GeneralPath p = new GeneralPath(); int numGeometries = polygon.getNumGeometries(); for( int i = 0; i < numGeometries; i++ ) { Geometry geometryN = polygon.getGeometryN(i); Coordinate[] coordinates = geometryN.getCoordinates(); final Point2D newP = new Point2D.Double(); for( int j = 0; j < coordinates.length; j++ ) { pointTransformation.transform(coordinates[j], newP); double x = newP.getX(); double y = newP.getY(); if (x < 0) { x = 0; } if (y < 0) { y = 0; } if (x > TILESIZE) { x = TILESIZE; } if (y > TILESIZE) { y = TILESIZE; } if (j == 0) { p.moveTo(x, y); } else { p.lineTo(x, y); } } } p.closePath(); return p; } private static void drawSources( ASpatialDb db, PointTransformation pointTransformation, Geometry sourcesUnion, Graphics2D gr, int finalTileSize ) throws Exception { // ShapeWriter sw = new ShapeWriter(pointTransformation); // Shape shape = sw.toShape(sourcesUnion); gr.setPaint(Color.red); GeneralPath p = polygonToPath(pointTransformation, sourcesUnion, finalTileSize); gr.draw(p); gr.fill(p); } public String toString() { return layerName; } @Override public Coordinate getCenter() { return centre; } }