/* * Copyright (C) 2011 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program 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.esa.snap.rcp.statistics; import com.bc.ceres.swing.TableLayout; import org.esa.snap.core.datamodel.BasicPixelGeoCoding; import org.esa.snap.core.datamodel.CombinedFXYGeoCoding; import org.esa.snap.core.datamodel.CrsGeoCoding; import org.esa.snap.core.datamodel.FXYGeoCoding; import org.esa.snap.core.datamodel.GcpGeoCoding; import org.esa.snap.core.datamodel.GeoCoding; import org.esa.snap.core.datamodel.GeoPos; import org.esa.snap.core.datamodel.MapGeoCoding; import org.esa.snap.core.datamodel.PixelPos; import org.esa.snap.core.datamodel.Placemark; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductNodeEvent; import org.esa.snap.core.datamodel.ProductNodeGroup; import org.esa.snap.core.datamodel.RasterDataNode; import org.esa.snap.core.datamodel.TiePointGeoCoding; import org.esa.snap.core.dataop.maptransf.MapInfo; import org.esa.snap.core.param.Parameter; import org.esa.snap.core.util.math.FXYSum; import org.openide.windows.TopComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Rectangle; /** * @author Thomas Storm * @author Tonio Fincke */ class GeoCodingPanel extends PagePanel { private static final String DEFAULT_INFORMATION_TEXT = "No geo-coding information available."; /*I18N*/ private static final String TITLE_PREFIX = "Geo-Coding"; /*I18N*/ private GeoCoding geoCoding; private JPanel contentPanel; private TableLayout contentLayout; private int currentRow; private StringBuilder dataAsTextBuilder; public GeoCodingPanel(TopComponent topComponent, String helpId) { super(topComponent, helpId, TITLE_PREFIX); } @Override protected boolean mustHandleSelectionChange() { final RasterDataNode raster = getRaster(); return super.mustHandleSelectionChange() || (raster != null && geoCoding != raster.getGeoCoding()); } @Override public void nodeChanged(final ProductNodeEvent event) { if (Product.PROPERTY_NAME_SCENE_GEO_CODING.equals(event.getPropertyName())) { if (event.getSourceNode() instanceof Product) { geoCoding = getProduct().getSceneGeoCoding(); } else { geoCoding = getRaster().getGeoCoding(); } updateComponents(); } } @Override protected void initComponents() { contentPanel = new JPanel(); resetContentPanel(); final JScrollPane scrollPane = new JScrollPane(contentPanel); scrollPane.getHorizontalScrollBar().setUnitIncrement(20); scrollPane.getVerticalScrollBar().setUnitIncrement(20); add(scrollPane, BorderLayout.CENTER); } @Override protected void updateComponents() { if (isVisible()) { currentRow = 0; updateContent(); if (geoCoding == null) { contentLayout.setColumnWeightX(0, 1.0); showNoInformationAvailableMessage(); } updateUI(); } } private void resetContentPanel() { contentPanel.removeAll(); contentLayout = new TableLayout(6); contentLayout.setTablePadding(2, 2); contentLayout.setTableFill(TableLayout.Fill.BOTH); contentLayout.setColumnWeightX(0, 0.0); contentLayout.setTableWeightX(1.0); contentLayout.setTableWeightY(0.0); contentLayout.setTableAnchor(TableLayout.Anchor.NORTHWEST); contentPanel.setLayout(contentLayout); } private void showNoInformationAvailableMessage() { contentPanel.add(new JLabel(DEFAULT_INFORMATION_TEXT)); contentPanel.add(contentLayout.createVerticalSpacer()); dataAsTextBuilder.append(DEFAULT_INFORMATION_TEXT); } @Override protected String getDataAsText() { return dataAsTextBuilder.toString(); } private void updateContent() { resetContentPanel(); dataAsTextBuilder = new StringBuilder(); final RasterDataNode raster = getRaster(); final Product product = getProduct(); boolean usingUniformGeoCodings = false; if (product != null) { usingUniformGeoCodings = product.isUsingSingleGeoCoding(); } final PixelPos sceneCenter; final PixelPos sceneUL; final PixelPos sceneUR; final PixelPos sceneLL; final PixelPos sceneLR; final String nodeType; if (usingUniformGeoCodings) { nodeType = "product"; geoCoding = product.getSceneGeoCoding(); sceneCenter = new PixelPos(Math.floor(product.getSceneRasterWidth() / 2.0) + 0.5, Math.floor(product.getSceneRasterHeight() / 2.0) + 0.5); sceneUL = new PixelPos(0 + 0.5f, 0 + 0.5f); sceneUR = new PixelPos(product.getSceneRasterWidth() - 1 + 0.5f, 0 + 0.5f); sceneLL = new PixelPos(0 + 0.5f, product.getSceneRasterHeight() - 1 + 0.5f); sceneLR = new PixelPos(product.getSceneRasterWidth() - 1 + 0.5f, product.getSceneRasterHeight() - 1 + 0.5f); } else { if (raster == null) { return; } nodeType = "band"; geoCoding = raster.getGeoCoding(); sceneCenter = new PixelPos(Math.floor(raster.getRasterWidth() / 2.0) + 0.5, Math.floor(raster.getRasterHeight() / 2.0) + 0.5); sceneUL = new PixelPos(0 + 0.5, 0 + 0.5); sceneUR = new PixelPos(raster.getRasterWidth() - 1 + 0.5, 0 + 0.5); sceneLL = new PixelPos(0 + 0.5, raster.getRasterHeight() - 1 + 0.5); sceneLR = new PixelPos(raster.getRasterWidth() - 1 + 0.5, raster.getRasterHeight() - 1 + 0.5); } writeGeoCoding(geoCoding, sceneCenter, sceneUL, sceneUR, sceneLL, sceneLR, nodeType); } private void writeGeoCoding(final GeoCoding geoCoding, final PixelPos sceneCenter, final PixelPos sceneUpperLeft, final PixelPos sceneUpperRight, final PixelPos sceneLowerLeft, final PixelPos sceneLowerRight, final String nodeType) { if (geoCoding != null) { GeoPos gp = new GeoPos(); gp = geoCoding.getGeoPos(sceneCenter, gp); addRow("Center latitude", gp.getLatString()); addRow("Center longitude", gp.getLonString()); gp = geoCoding.getGeoPos(sceneUpperLeft, gp); addRow("Upper left latitude", gp.getLatString()); addRow("Upper left longitude", gp.getLonString()); gp = geoCoding.getGeoPos(sceneUpperRight, gp); addRow("Upper right latitude", gp.getLatString()); addRow("Upper right longitude", gp.getLonString()); gp = geoCoding.getGeoPos(sceneLowerLeft, gp); addRow("Lower left latitude", gp.getLatString()); addRow("Lower left longitude", gp.getLonString()); gp = geoCoding.getGeoPos(sceneLowerRight, gp); addRow("Lower right latitude", gp.getLatString()); addRow("Lower right longitude", gp.getLonString()); addEmptyRow(); addRowWithTextField("WKT of the image CRS", geoCoding.getImageCRS().toString()); addRowWithTextField("WKT of the geographical CRS", geoCoding.getGeoCRS().toString()); addEmptyRow(); } if (geoCoding instanceof TiePointGeoCoding) { writeTiePointGeoCoding((TiePointGeoCoding) geoCoding, nodeType); } else if (geoCoding instanceof BasicPixelGeoCoding) { writePixelGeoCoding((BasicPixelGeoCoding) geoCoding, nodeType); } else if (geoCoding instanceof MapGeoCoding) { writeMapGeoCoding((MapGeoCoding) geoCoding, nodeType); } else if (geoCoding instanceof FXYGeoCoding) { writeFXYGeoCoding((FXYGeoCoding) geoCoding, nodeType); } else if (geoCoding instanceof CombinedFXYGeoCoding) { writeCombinedFXYGeoCoding((CombinedFXYGeoCoding) geoCoding, nodeType); } else if (geoCoding instanceof GcpGeoCoding) { writeGcpGeoCoding((GcpGeoCoding) geoCoding, nodeType); } else if (geoCoding instanceof CrsGeoCoding) { writeCrsGeoCoding((CrsGeoCoding) geoCoding, nodeType); } else if (geoCoding != null) { writeUnknownGeoCoding(geoCoding, nodeType); } else { addRow("The " + nodeType + " has no geo-coding information."); } } private void addHeaderRow(String content) { StringBuilder b = new StringBuilder(); for (int i = 0; i < content.length(); i++) { b.append('='); } contentLayout.setCellColspan(currentRow++, 0, 6); contentPanel.add(getCorrectlyColouredLabel(b.toString())); contentLayout.setCellColspan(currentRow++, 0, 6); contentPanel.add(getCorrectlyColouredLabel(content)); contentLayout.setCellColspan(currentRow++, 0, 6); contentPanel.add(getCorrectlyColouredLabel(b.toString())); dataAsTextBuilder.append(b.toString()).append("/n").append(content).append("/n").append(b.toString()).append("/n"); } private void addRow(String content) { contentLayout.setCellColspan(currentRow++, 0, 6); contentPanel.add(getCorrectlyColouredLabel(content)); dataAsTextBuilder.append(content).append("/n"); } private void addRow(String name, String value) { contentLayout.setCellColspan(currentRow++, 1, 5); contentPanel.add(getCorrectlyColouredLabel(name)); contentPanel.add(getCorrectlyColouredLabel(value)); dataAsTextBuilder.append(name).append("/t").append(value).append("/n"); } private void addRowWithTextField(String name, String value) { contentLayout.setCellColspan(currentRow++, 1, 5); contentPanel.add(getCorrectlyColouredLabel(name)); final JTextArea textArea = new JTextArea(value); textArea.setBackground(getBackgroundColor()); textArea.setEditable(false); contentPanel.add(textArea); dataAsTextBuilder.append(name).append("/t").append(value).append("/n"); } private void addEmptyRow() { contentPanel.add(contentLayout.createVerticalSpacer()); currentRow++; dataAsTextBuilder.append("/n"); } private void addRow(String... values) { for (String value : values) { contentPanel.add(getCorrectlyColouredLabel(value)); dataAsTextBuilder.append(value).append("/t"); } currentRow++; dataAsTextBuilder.append("/n"); } private JLabel getCorrectlyColouredLabel(String value) { final JLabel label = new JLabel(value); label.setBackground(getBackgroundColor()); label.setOpaque(true); return label; } private Color getBackgroundColor() { final Color white = Color.WHITE; if (currentRow % 2 == 0) { return new Color((14 * white.getRed()) / 15, (14 * white.getGreen()) / 15, (14 * white.getBlue()) / 15); } return white; } private void writeGcpGeoCoding(GcpGeoCoding gcpGeoCoding, String nodeType) { addEmptyRow(); addRow("The " + nodeType + " uses a geo-coding which is based on ground control points (GCPs)."); addEmptyRow(); ProductNodeGroup<Placemark> gcpGroup = getProduct().getGcpGroup(); addRow("Number Of GCPs", String.valueOf(gcpGroup.getNodeCount())); addRow("Function", String.valueOf(gcpGeoCoding.getMethod())); addRow("Datum", String.valueOf(gcpGeoCoding.getDatum().getName())); addRow("Latitude RMSE", String.valueOf(gcpGeoCoding.getRmseLat())); addRow("Longitude RMSE", String.valueOf(gcpGeoCoding.getRmseLon())); addEmptyRow(); addRow("Table of used GCPs"); Placemark[] gcps = gcpGroup.toArray(new Placemark[0]); addRow("Number", "Label", "X", "Y", "Latitude", "Longitude"); for (int i = 0; i < gcps.length; i++) { Placemark gcp = gcps[i]; PixelPos pixelPos = gcp.getPixelPos(); GeoPos geoPos = gcp.getGeoPos(); addRow(String.valueOf(i), gcp.getLabel(), String.valueOf(pixelPos.getX()), String.valueOf(pixelPos.getY()), geoPos.getLatString(), geoPos.getLonString()); } // setFirstColumnWidth(40); } private void writeCrsGeoCoding(CrsGeoCoding geoCoding, String nodeType) { addRow("The " + nodeType + " uses a geo-coding based on a cartographic map CRS."); addEmptyRow(); addRow("WKT of the map CRS", geoCoding.getMapCRS().toString()); addEmptyRow(); addRow("Image-to-map transformation", geoCoding.getImageToMapTransform().toString()); } private void writeUnknownGeoCoding(GeoCoding geoCoding, String nodeType) { addRow("The " + nodeType + " uses an unknown geo-coding implementation."); addRow("Class", geoCoding.getClass().getName()); addRow("Instance", geoCoding.toString()); } private void writeCombinedFXYGeoCoding(CombinedFXYGeoCoding combinedGeoCoding, String nodeType) { final CombinedFXYGeoCoding.CodingWrapper[] codingWrappers = combinedGeoCoding.getCodingWrappers(); addEmptyRow(); addRow("The " + nodeType + " uses a geo-coding which consists of multiple polynomial based geo-coding."); addEmptyRow(); addRow("The geo-coding uses " + codingWrappers.length + " polynomial based geo-codings"); for (int i = 0; i < codingWrappers.length; i++) { final CombinedFXYGeoCoding.CodingWrapper codingWrapper = codingWrappers[i]; final Rectangle region = codingWrapper.getRegion(); addHeaderRow("Geo-coding[" + (i + 1) + "]"); addRow("The region in the scene which is covered by this geo-coding is defined by:"); addRow("Location: X = " + region.x + ", Y = " + region.y + "\n"); addRow("Dimension: W = " + region.width + ", H = " + region.height); addEmptyRow(); final FXYGeoCoding fxyGeoCoding = codingWrapper.getGeoGoding(); addRow("<html>Geographic coordinates (lat,lon) are computed from pixel coordinates (x,y)<br/>" + "by using following polynomial equations</html>"); addRow(fxyGeoCoding.getLatFunction().createCFunctionCode("latitude", "x", "y")); addRow(fxyGeoCoding.getLonFunction().createCFunctionCode("longitude", "x", "y")); addEmptyRow(); addRow("<html>Pixels (x,y) are computed from geographic coordinates (lat,lon)<br/>" + "by using the following polynomial equations</html>"); addRow(fxyGeoCoding.getPixelXFunction().createCFunctionCode("x", "lat", "lon")); addRow(fxyGeoCoding.getPixelYFunction().createCFunctionCode("y", "lat", "lon")); } } private void writeFXYGeoCoding(FXYGeoCoding fxyGeoCoding, String nodeType) { addEmptyRow(); addRow("The" + nodeType + " uses a polynomial based geo-coding."); addEmptyRow(); addRow("<html>Geographic coordinates (lat,lon) are computed from pixel coordinates (x,y)<br/>" + "by using following polynomial equations</html>"); addRow(fxyGeoCoding.getLatFunction().createCFunctionCode("latitude", "x", "y")); addRow(fxyGeoCoding.getLonFunction().createCFunctionCode("longitude", "x", "y")); addEmptyRow(); addRow("<html>Pixels (x,y) are computed from geographic coordinates (lat,lon)<br/>" + "by using the following polynomial equations</html>"); addRow(fxyGeoCoding.getPixelXFunction().createCFunctionCode("x", "lat", "lon")); addRow(fxyGeoCoding.getPixelYFunction().createCFunctionCode("y", "lat", "lon")); } private void writeMapGeoCoding(MapGeoCoding mgc, String nodeType) { final MapInfo mi = mgc.getMapInfo(); addEmptyRow(); addRow("The " + nodeType + " uses a map-projection based geo-coding."); addEmptyRow(); addRow("Projection", mi.getMapProjection().getName()); addRow("Projection parameters"); final Parameter[] parameters = mi.getMapProjection().getMapTransform().getDescriptor().getParameters(); final double[] parameterValues = mi.getMapProjection().getMapTransform().getParameterValues(); for (int i = 0; i < parameters.length; i++) { addRow(parameters[i].getName(), String.valueOf(parameterValues[i]) + " " + parameters[i].getProperties().getPhysicalUnit()); } addEmptyRow(); addRow("Map CRS Name", mgc.getMapCRS().getName().toString()); addRow("Map CRS WKT"); addRow(mgc.getMapCRS().toWKT()); addEmptyRow(); addRow("Output parameters"); addRow("Datum", mi.getDatum().getName()); addRow("Reference pixel X", String.valueOf(mi.getPixelX())); addRow("Reference pixel Y", String.valueOf(mi.getPixelY())); addRow("Orientation", String.valueOf(mi.getOrientation()) + " degree"); String mapUnit = mi.getMapProjection().getMapUnit(); addRow("Northing", String.valueOf(mi.getNorthing()) + " " + mapUnit); addRow("Easting", String.valueOf(mi.getEasting()) + " " + mapUnit); addRow("Pixel size X", String.valueOf(mi.getPixelSizeX()) + " " + mapUnit); addRow("Pixel size Y", String.valueOf(mi.getPixelSizeY()) + " " + mapUnit); } private void writePixelGeoCoding(BasicPixelGeoCoding gc, String nodeType) { addEmptyRow(); addRow("The " + nodeType + " uses a pixel based geo-coding."); addEmptyRow(); addRow("Name of latitude band", gc.getLatBand().getName()); addRow("Name of longitude band", gc.getLonBand().getName()); addRow("Search radius", gc.getSearchRadius() + " pixels"); final String validMask = gc.getValidMask(); addRow("Valid pixel mask", validMask != null ? validMask : ""); addRow("Crossing 180 degree meridian", String.valueOf(gc.isCrossingMeridianAt180())); addEmptyRow(); addRow("<html>Geographic coordinates (lat,lon) are computed from pixel coordinates (x,y)<br/>" + "by linear interpolation between pixels.</html>"); addEmptyRow(); addRow("<html>Pixel coordinates (x,y) are computed from geographic coordinates (lat,lon)<br/>" + "by a search algorithm.</html>"); addEmptyRow(); } private void writeTiePointGeoCoding(TiePointGeoCoding tgc, String nodeType) { addRow("The " + nodeType + " uses a tie-point based geo-coding."); addEmptyRow(); addRow("Name of latitude tie-point grid", tgc.getLatGrid().getName()); addRow("Name of longitude tie-point grid", tgc.getLonGrid().getName()); addRow("Crossing 180 degree meridian", String.valueOf(tgc.isCrossingMeridianAt180())); addEmptyRow(); addRow("<html>Geographic coordinates (lat,lon) are computed from pixel coordinates (x,y)<br/>" + "by linear interpolation between tie points.</html>"); final int numApproximations = tgc.getNumApproximations(); if (numApproximations > 0) { addRow("<html>Pixel coordinates (x,y) are computed from geographic coordinates (lat,lon)<br/>" + "by polynomial approximations for " + numApproximations + " tile(s).</html>"); addEmptyRow(); for (int i = 0; i < numApproximations; i++) { final TiePointGeoCoding.Approximation approximation = tgc.getApproximation(i); final FXYSum fX = approximation.getFX(); final FXYSum fY = approximation.getFY(); addHeaderRow("Approximation for tile " + (i + 1)); addRow("Center latitude", String.valueOf(approximation.getCenterLat()) + " degree"); addRow("Center longitude", String.valueOf(approximation.getCenterLon()) + " degree"); addRow("RMSE for X", String.valueOf(fX.getRootMeanSquareError()) + " pixels"); addRow("RMSE for Y", String.valueOf(fY.getRootMeanSquareError()) + " pixels"); addRow("Max. error for X", String.valueOf(fX.getMaxError()) + " pixels"); addRow("Max. error for Y", String.valueOf(fY.getMaxError()) + " pixels"); } } else { addEmptyRow(); addRow( "<html>WARNING: Pixel coordinates (x,y) cannot be computed from geographic coordinates (lat,lon)<br/>" + "because appropriate polynomial approximations could not be found.</html>"); } } }