package org.ianturton.cookbook.projections; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import javax.imageio.ImageIO; import javax.imageio.stream.ImageOutputStream; import javax.swing.AbstractAction; import javax.swing.JOptionPane; import javax.swing.JToolBar; import org.geotools.data.FileDataStore; import org.geotools.data.FileDataStoreFinder; import org.geotools.data.collection.ListFeatureCollection; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.grid.Grids; import org.geotools.map.FeatureLayer; import org.geotools.map.Layer; import org.geotools.map.MapContent; import org.geotools.styling.SLD; import org.geotools.styling.Style; import org.geotools.swing.JMapFrame; import org.geotools.swing.JMapPane; import org.geotools.swing.data.JFileDataStoreChooser; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.FeatureType; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Point; public class Tissot { private static final String DEFAULT_GEOMETRY_ATTRIBUTE_NAME = "the_geom"; private JMapFrame frame; private MapContent mapContent; public static void main(String[] args) throws IOException { File file = null; if (args.length == 0) { // display a data store file chooser dialog for shapefiles file = JFileDataStoreChooser.showOpenFile("shp", null); if (file == null) { return; } } else { file = new File(args[0]); if (!file.exists()) { System.err.println(file + " doesn't exist"); return; } } new Tissot(file); } public Tissot(File file) throws IOException { FileDataStore store = FileDataStoreFinder.getDataStore(file); SimpleFeatureSource featureSource = store.getFeatureSource(); // Create a map content and add our shapefile to it mapContent = new MapContent(); mapContent.setTitle("GeoTools Mapping"); Style style = SLD.createSimpleStyle(featureSource.getSchema()); Layer layer = new FeatureLayer(featureSource, style); mapContent.addLayer(layer); ReferencedEnvelope gridBounds = layer.getBounds(); Layer gridLayer = createGridLayer(style, gridBounds); mapContent.addLayer(gridLayer); Style pstyle = SLD.createPointStyle("circle", Color.red, Color.red, 1.0f, 5.0f); Layer tissotLayer = createTissotLayer(pstyle, gridBounds); mapContent.addLayer(tissotLayer); frame = new JMapFrame(mapContent); frame.enableStatusBar(true); frame.enableToolBar(true); JToolBar toolBar = frame.getToolBar(); toolBar.addSeparator(); SaveAction save = new SaveAction("Save"); toolBar.add(save); frame.initComponents(); frame.setSize(1000, 500); frame.setVisible(true); } private FeatureType createFeatureType(String name, CoordinateReferenceSystem crs) { SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); String finalName; if (name == null || name.isEmpty()) { finalName = "Tissot"; } else { finalName = name; } tb.setName(finalName); tb.add(DEFAULT_GEOMETRY_ATTRIBUTE_NAME, Point.class, crs); tb.add("id", String.class); return tb.buildFeatureType(); } /** * A method to create Tissot Indicatrices based on Whuber's answer to * http://gis * .stackexchange.com/questions/5068/how-to-create-an-accurate-tissot * -indicatrix * * @param style * - the style to draw the circles with * @param gridBounds * - the bounds of the map (may be increased in the method) * @return a layer of Tissot Indicatrices (scaled for visibility). */ private Layer createTissotLayer(Style style, ReferencedEnvelope gridBounds) { FeatureType type = createFeatureType(null, gridBounds.getCoordinateReferenceSystem()); SimpleFeatureBuilder sfb = new SimpleFeatureBuilder( (SimpleFeatureType) type); double step = 20.0; GeometryFactory geomFac = new GeometryFactory(); double width = gridBounds.getWidth(); double height = gridBounds.getHeight(); int id = 0; final ListFeatureCollection fc = new ListFeatureCollection( (SimpleFeatureType) type); double y = gridBounds.getMinY(); for (int iy = 0; iy < (height / step); iy++) { double x = gridBounds.getMinX(); for (int ix = 0; ix < (width / step); ix++) { Point p = geomFac.createPoint(new Coordinate(x, y)); sfb.set("the_geom", p); SimpleFeature f = sfb.buildFeature("tissot2" + id); fc.add(f); x += step; } y += step; } Layer layer = new FeatureLayer(fc, style); return layer; } public void drawMapToImage(File outputFile, String outputType) { JMapPane mapPane = frame.getMapPane(); ImageOutputStream outputImageFile = null; FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(outputFile); outputImageFile = ImageIO.createImageOutputStream(fileOutputStream); RenderedImage bufferedImage = mapPane.getBaseImage(); ImageIO.write(bufferedImage, outputType, outputImageFile); } catch (IOException ex) { ex.printStackTrace(); } finally { try { if (outputImageFile != null) { outputImageFile.flush(); outputImageFile.close(); fileOutputStream.flush(); fileOutputStream.close(); } } catch (IOException e) {// don't care now } } } private class SaveAction extends AbstractAction { /** * Private SaveAction */ private static final long serialVersionUID = 3071568727121984649L; public SaveAction(String text) { super(text); } public void actionPerformed(ActionEvent arg0) { String[] writers = ImageIO.getWriterFormatNames(); String format = (String) JOptionPane.showInputDialog(frame, "Choose output format:", "Customized Dialog", JOptionPane.PLAIN_MESSAGE, null, writers, "png"); drawMapToImage(new File("ian." + format), format); } } private Layer createGridLayer(Style style, ReferencedEnvelope gridBounds) throws IOException { double squareWidth = 20.0; double extent = gridBounds.maxExtent(); double ll = Math.log10(extent); if (ll > 0) { // there are ll 10's across the map while (ll-- > 4) { squareWidth *= 10; } } // max distance between vertices double vertexSpacing = squareWidth / 20; // grow to cover the whole map (and a bit). double left = gridBounds.getMinX(); double bottom = gridBounds.getMinY(); if (left % squareWidth != 0) { if (left > 0.0) { // east left -= Math.abs(left % squareWidth); } else { // west left += Math.abs(left % squareWidth); } } if (bottom % squareWidth != 0) { if (bottom > 0.0) { bottom -= Math.abs(bottom % squareWidth); } else { bottom += Math.abs(bottom % squareWidth); } } gridBounds.expandToInclude(left, bottom); double right = gridBounds.getMaxX(); double top = gridBounds.getMaxY(); if (right % squareWidth != 0) { if (right > 0.0) { // east right += Math.abs(right % squareWidth) + squareWidth; } else { // west right -= Math.abs(right % squareWidth) - squareWidth; } } if (top % squareWidth != 0) { if (top > 0.0) { // North top += Math.abs(top % squareWidth) + squareWidth; } else { // South top -= Math.abs(top % squareWidth) - squareWidth; } } gridBounds.expandToInclude(right, top); SimpleFeatureSource grid = Grids.createSquareGrid(gridBounds, squareWidth, vertexSpacing); Layer gridLayer = new FeatureLayer(grid.getFeatures(), style); return gridLayer; } }