/* * Copyright (c) 2016 Metron, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Metron, Inc. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.metsci.glimpse.charts.shoreline; import java.awt.Shape; import java.awt.geom.PathIterator; import java.awt.geom.Point2D.Double; import java.awt.geom.Rectangle2D; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import com.metsci.glimpse.axis.Axis2D; import com.metsci.glimpse.charts.shoreline.LandShape.VertexConverter; import com.metsci.glimpse.charts.shoreline.ndgc.NgdcFile2; import com.metsci.glimpse.context.GlimpseContext; import com.metsci.glimpse.painter.base.GlimpsePainterBase; import com.metsci.glimpse.painter.shape.PolygonPainter; import com.metsci.glimpse.support.polygon.Polygon; import com.metsci.glimpse.support.polygon.Polygon.Interior; import com.metsci.glimpse.support.polygon.Polygon.Loop.LoopBuilder; import com.metsci.glimpse.util.geo.LatLonGeo; import com.metsci.glimpse.util.geo.projection.GeoProjection; import com.metsci.glimpse.util.vector.Vector2d; /** * Originally designed to display shoreline data available from the NOAA/NGDC Coastline Extractor * tool at http://www.ngdc.noaa.gov/mgg_coastline/</p> * * Now capable of painting all LandShapes, NdgcFile shapes as wells as Gshhs shapes. * * When downloading ndgc data, choose the following options: * World Vector Shoreline * Compression Method: None * Coast Format: Mapgen * Coast Preview: No Preview * * * @author ulman * @author cunningham */ public class LandShapePainter extends GlimpsePainterBase { protected static final int LAND_GROUP_ID = 1337; protected PolygonPainter polygonPainter; protected int landPolygonCounter = 0; protected Rectangle2D bounds; public LandShapePainter( ) { this.polygonPainter = new PolygonPainter( ); this.setFillColor( 151 / 255.0f, 152 / 255.0f, 107 / 255.0f, 1.0f ); this.setLineColor( 112 / 255.0f, 140 / 255.0f, 76 / 255.0f, 1.0f ); this.setLineWidth( 2 ); this.setFill( true ); } /** * Deprecated in favor of loadNdgcLandFile( InputStream in, GeoProjection geoProjection ) * * @param in * @param geoProjection * @throws IOException */ @Deprecated public void loadLandFile( InputStream in, GeoProjection geoProjection ) throws IOException { loadNdgcLandFile( in, geoProjection ); } public void loadNdgcLandFile( InputStream in, GeoProjection geoProjection ) throws IOException { loadNgdcLandFile0( in, geoProjection ); } /** * Deprecated in favor of loadNgdcLandFile( String file, GeoProjection geoProjection ) * * @param file * @param geoProjection * @throws IOException */ @Deprecated public void loadLandFile( String file, GeoProjection geoProjection ) throws IOException { loadNgdcLandFile( file, geoProjection ); } public void loadNgdcLandFile( String file, GeoProjection geoProjection ) throws IOException { loadNgdcLandFile0( new FileInputStream( file ), geoProjection ); } /** * Deprecated in favor of loadNgdcLandFile( File file, GeoProjection geoProjection ) * @param file * @param geoProjection * @throws IOException */ @Deprecated public void loadLandFile( File file, GeoProjection geoProjection ) throws IOException { loadNgdcLandFile( file, geoProjection ); } public void loadNgdcLandFile( File file, GeoProjection geoProjection ) throws IOException { loadNgdcLandFile0( new FileInputStream( file ), geoProjection ); } /** * Deprecated in favor of loadNgdcLandFileAndCenterAxis( File file, GeoProjection geoProjection, Axis2D axis ) * @param file * @param geoProjection * @param axis * @throws IOException */ @Deprecated public void loadLandFileAndCenterAxis( File file, GeoProjection geoProjection, Axis2D axis ) throws IOException { loadNgdcLandFileAndCenterAxis( file, geoProjection, axis ); } public void loadNgdcLandFileAndCenterAxis( InputStream in, GeoProjection geoProjection, Axis2D axis ) throws IOException { NgdcFile2 ngdcFile = new NgdcFile2( in ); Shape shape = loadLandFile0( ngdcFile.toShape( ), geoProjection ); centerAxesOnShape( shape, axis ); } public void loadNgdcLandFileAndCenterAxis( File file, GeoProjection geoProjection, Axis2D axis ) throws IOException { NgdcFile2 ngdcFile = new NgdcFile2( new FileInputStream( file ) ); loadLandFileAndCenterAxis( ngdcFile, geoProjection, axis ); } public void loadLandFileAndCenterAxis( LandShapeCapable landFile, GeoProjection geoProjection, Axis2D axis ) throws IOException { loadLandFileAndCenterAxis( landFile.toShape( ), geoProjection, axis ); } public void loadLandFileAndCenterAxis( LandShape landShape, GeoProjection geoProjection, Axis2D axis ) throws IOException { Shape shape = loadLandFile0( landShape, geoProjection ); centerAxesOnShape( shape, axis ); } public void centerAxesOnShape( Shape shape, Axis2D axis ) { Rectangle2D localBounds = shape.getBounds2D( ); if ( bounds == null ) { bounds = new Rectangle2D.Double( ); bounds.setRect( localBounds ); } else { bounds.add( localBounds ); } axis.getAxisX( ).setMin( bounds.getMinX( ) ); axis.getAxisX( ).setMax( bounds.getMaxX( ) ); axis.getAxisY( ).setMin( bounds.getMinY( ) ); axis.getAxisY( ).setMax( bounds.getMaxY( ) ); } protected Shape loadNgdcLandFile0( InputStream in, final GeoProjection geoProjection ) throws IOException { NgdcFile2 ngdcFile = new NgdcFile2( in ); return loadLandFile0( ngdcFile.toShape( ), geoProjection ); } protected Shape loadLandFile0( LandFile landFile, final GeoProjection geoProjection ) throws IOException { return loadLandFile0( landFile.toShape( ), geoProjection ); } protected Shape loadLandFile0( LandShape landShape, final GeoProjection geoProjection ) { Shape shape = landShape.getFillShape( new VertexConverter( ) { @Override public void toXY( double lat, double lon, Double xy ) { Vector2d vector = geoProjection.project( LatLonGeo.fromDeg( lat, lon ) ); xy.x = vector.getX( ); xy.y = vector.getY( ); } } ); //XXX Here we load every Shape segment as a different polygon //XXX This won't work for shapes with holes, still need to figure this out Polygon p = new Polygon( ); PathIterator iter = shape.getPathIterator( null ); double[] vertices = new double[6]; LoopBuilder b = new LoopBuilder( ); while ( !iter.isDone( ) ) { iter.next( ); int type = iter.currentSegment( vertices ); if ( type == PathIterator.SEG_CLOSE ) { p.add( b.complete( Interior.onRight ) ); addPolygon( p ); p = new Polygon( ); b = new LoopBuilder( ); } else if ( type == PathIterator.SEG_LINETO ) { b.addVertices( vertices, 1 ); } else if ( type == PathIterator.SEG_MOVETO ) { p.add( b.complete( Interior.onRight ) ); addPolygon( p ); p = new Polygon( ); b = new LoopBuilder( ); b.addVertices( vertices, 1 ); } else { throw new UnsupportedOperationException( "Shape Not Supported." ); } } return shape; } protected void addPolygon( Polygon p ) { polygonPainter.addPolygon( LAND_GROUP_ID, landPolygonCounter++, Long.MIN_VALUE, Long.MAX_VALUE, p, 0.0f ); } public void setLineColor( float r, float g, float b, float a ) { polygonPainter.setLineColor( LAND_GROUP_ID, r, g, b, a ); } public void setLineColor( float[] rgba ) { polygonPainter.setLineColor( LAND_GROUP_ID, rgba ); } public void setLineWidth( int width ) { polygonPainter.setLineWidth( LAND_GROUP_ID, width ); } public void setShowLines( boolean show ) { polygonPainter.setShowLines( LAND_GROUP_ID, show ); } public void setPolyDotted( byte[] stipple ) { polygonPainter.setPolyDotted( LAND_GROUP_ID, stipple ); } public void setPolyDotted( boolean dotted ) { polygonPainter.setPolyDotted( LAND_GROUP_ID, dotted ); } public void setLineDotted( boolean dotted ) { polygonPainter.setLineDotted( LAND_GROUP_ID, dotted ); } public void setLineDotted( int stippleFactor, short stipplePattern ) { polygonPainter.setLineDotted( LAND_GROUP_ID, stippleFactor, stipplePattern ); } public void setFill( boolean show ) { polygonPainter.setFill( LAND_GROUP_ID, show ); } public void setFillColor( float[] rgba ) { polygonPainter.setFillColor( LAND_GROUP_ID, rgba ); } public void setFillColor( float r, float g, float b, float a ) { polygonPainter.setFillColor( LAND_GROUP_ID, r, g, b, a ); } public void deleteAll( ) { polygonPainter.deleteAll( ); } @Override public void doDispose( GlimpseContext context ) { polygonPainter.doDispose( context ); } @Override public void doPaintTo( GlimpseContext context ) { polygonPainter.doPaintTo( context ); } }