/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2014, Geomatys * * 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.geotoolkit.gui.swing.render2d.control.information.presenter; import org.apache.sis.referencing.CommonCRS; import org.geotoolkit.storage.coverage.CoverageExtractor; import org.geotoolkit.storage.coverage.CoverageReference; import org.geotoolkit.coverage.GridSampleDimension; import org.geotoolkit.coverage.grid.GeneralGridGeometry; import org.geotoolkit.coverage.io.CoverageStoreException; import org.geotoolkit.coverage.io.GridCoverageReader; import org.geotoolkit.display2d.canvas.AbstractGraphicVisitor; import org.geotoolkit.display2d.canvas.RenderingContext2D; import org.geotoolkit.display2d.primitive.ProjectedCoverage; import org.geotoolkit.display2d.primitive.SearchAreaJ2D; import org.geotoolkit.map.CoverageMapLayer; import org.apache.sis.referencing.CRS; import org.geotoolkit.referencing.ReferencingUtilities; import org.opengis.geometry.DirectPosition; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.TemporalCRS; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import org.opengis.util.FactoryException; import javax.measure.Unit; import javax.swing.*; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.StyleSheet; import java.awt.*; import java.awt.event.ActionEvent; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.*; import java.util.logging.Level; /** * @author Quentin Boileau (Geomatys) */ public class CoverageDrillingPresenter extends AbstractInformationPresenter { public CoverageDrillingPresenter() { super(10); } @Override public JComponent createComponent(Object graphic, RenderingContext2D context, SearchAreaJ2D area) { if (graphic instanceof ProjectedCoverage) { final ProjectedCoverage projectedCoverage = (ProjectedCoverage) graphic; final CoverageMapLayer layer = projectedCoverage.getLayer(); final CoverageReference covRef = layer.getCoverageReference(); GridCoverageReader reader = null; try { reader = covRef.acquireReader(); final GeneralGridGeometry gridGeo = reader.getGridGeometry(covRef.getImageIndex()); final CoordinateReferenceSystem crs = gridGeo.getCoordinateReferenceSystem(); final int dimension = crs.getCoordinateSystem().getDimension(); if (dimension > 2) { return createComponent(projectedCoverage, context, area); } } catch (CoverageStoreException ex) { context.getMonitor().exceptionOccured(ex, Level.INFO); return null; } finally { if (reader != null) { covRef.recycle(reader); } } } return null; } private JComponent createComponent(final ProjectedCoverage projectedCoverage, final RenderingContext2D context, final SearchAreaJ2D area) { final JPanel container = new JPanel(new BorderLayout()); container.add(new JLabel("Loading ..."), BorderLayout.CENTER); container.setSize(350, 200); new Thread(new Runnable() { @Override public void run() { //extract values CoverageExtractor.Ray allValues = null; CoordinateReferenceSystem crs = null; try { allValues = Bridge.extractData(projectedCoverage, context, area); final CoverageMapLayer layer = projectedCoverage.getLayer(); final CoverageReference covRef = layer.getCoverageReference(); final GridCoverageReader reader = covRef.acquireReader(); final GeneralGridGeometry gridGeo = reader.getGridGeometry(covRef.getImageIndex()); crs = gridGeo.getCoordinateReferenceSystem(); covRef.recycle(reader); if (allValues == null || allValues.getValues().isEmpty()) { container.removeAll(); container.add(new JLabel("No values")); } //analyse Map<Integer, CoordinateReferenceSystem> analyse = ReferencingUtilities.indexedDecompose(crs); TemporalCRS temporalCRS = null; int timeIdx = -1; for (Map.Entry<Integer, CoordinateReferenceSystem> entry : analyse.entrySet()) { if (entry.getValue() instanceof TemporalCRS) { temporalCRS = (TemporalCRS) entry.getValue(); timeIdx = entry.getKey(); break; } } MathTransform transform = null; double[] timesArray = new double[0]; if (temporalCRS != null) { transform = CRS.findOperation(temporalCRS, CommonCRS.Temporal.JAVA.crs(), null).getMathTransform(); final Set<Double> times = new TreeSet<Double>(); Set<DirectPosition> positions = allValues.getValues().keySet(); for (DirectPosition position : positions) { times.add(position.getOrdinate(timeIdx)); } timesArray = new double[times.size()]; int i = 0; for (Double time : times) { timesArray[i++] = time; } } //return panel container.removeAll(); container.add(new ResultPanel(allValues, analyse, transform, timeIdx, timesArray), BorderLayout.CENTER); } catch (CoverageStoreException ex) { context.getMonitor().exceptionOccured(ex, Level.INFO); container.removeAll(); container.add(new JLabel("Error : " + ex.getMessage())); } catch (FactoryException ex) { context.getMonitor().exceptionOccured(ex, Level.INFO); container.removeAll(); container.add(new JLabel("Error : " + ex.getMessage())); } catch (TransformException ex) { context.getMonitor().exceptionOccured(ex, Level.INFO); container.removeAll(); container.add(new JLabel("Error : " + ex.getMessage())); } container.revalidate(); container.repaint(); container.firePropertyChange("update", false, true); } }).start(); return container; } private class ResultPanel extends JPanel { private final Map<DirectPosition, double[]> allValues; private final MathTransform temporalToJava; private final int timeIdx; private final double[] times; private final int[] dimIndexes; private final NumberFormat nbFormat = new DecimalFormat(); private final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); private final String header; private final JPanel contentPane; private final JLabel label = new JLabel("0/0"); private final JTextPane textPane; private final AbstractAction nextAction; private final AbstractAction previousAction; private final JToolBar toolbar; private int currentTime = 0; private ResultPanel(CoverageExtractor.Ray result, Map<Integer, CoordinateReferenceSystem> crsParts, MathTransform temporalToJava, int timeIdx, double[] times) { super(new BorderLayout()); this.allValues = result.getValues(); this.temporalToJava = temporalToJava; this.timeIdx = timeIdx; this.times = times; final java.util.List<GridSampleDimension> dimensions = result.getSampleDimensions(); final Map.Entry<DirectPosition, double[]> firstEntry = allValues.entrySet().iterator().next(); final DirectPosition firstPos = firstEntry.getKey(); int nbBand = firstEntry.getValue().length; nbFormat.setMaximumFractionDigits(3); dateFormat.setTimeZone(TimeZone.getTimeZone("GMT0")); contentPane = new JPanel(new BorderLayout()); //CSS StyleSheet styles = new StyleSheet(); styles.addRule("body {padding:10px; background-color:#ffffff; font-family:Monospaced;}"); styles.addRule("table {margin-left: 15px; border-collapse: collapse; width:100%;}"); styles.addRule("tr {border-width: 1px; border-style:solid; border-color:black;}"); styles.addRule("td {border-width: 1px; text-align: center; border-style:solid; border-color:black; padding:5px;}"); styles.addRule("th {border-width: 1px; text-align: center; border-style:solid; border-color:black; padding:5px;}"); styles.addRule(".separator {border:2px; width:1px; padding:1px;}"); textPane = new JTextPane(new HTMLDocument(styles)); textPane.setEditable(false); textPane.setContentType("text/html"); textPane.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 11)); textPane.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR)); textPane.setStyledDocument(new HTMLDocument(styles)); final JScrollPane scroll = new JScrollPane(textPane); contentPane.add(BorderLayout.CENTER, scroll); final StringBuilder sb = new StringBuilder("<html><body>"); sb.append("<b>").append("λ : ").append(nbFormat.format(firstPos.getOrdinate(0))).append("</b><br/>"); sb.append("<b>").append("φ : ").append(nbFormat.format(firstPos.getOrdinate(1))).append("</b><br/>"); sb.append("<hr>"); sb.append("<table>"); sb.append("<tr>"); // index of dimensions not horizontal nor temporal LinkedList<Integer> dimIndexesList = new LinkedList<Integer>(); //CRS dim columns int i = 0; for (Map.Entry<Integer, CoordinateReferenceSystem> entry : crsParts.entrySet()) { final CoordinateReferenceSystem crsPart = entry.getValue(); if (!CRS.isHorizontalCRS(crsPart) && !(crsPart instanceof TemporalCRS)) { dimIndexesList.add(entry.getKey()); CoordinateSystem cs = crsPart.getCoordinateSystem(); CoordinateSystemAxis axis = cs.getAxis(0); sb.append("<th>").append(axis.getAbbreviation()); if (axis.getUnit() != null) { sb.append("(").append(axis.getUnit()).append(")"); } sb.append("</th>"); } } dimIndexes = new int[dimIndexesList.size()]; for (int j = 0; j < dimIndexesList.size(); j++) { dimIndexes[j] = dimIndexesList.get(j); } //band columns for (int j = 0; j < nbBand; j++) { GridSampleDimension dim = dimensions.get(j); Unit<?> units = dim.getUnits(); if (j == 0) { sb.append("<th class=\"separator\"></th>"); } sb.append("<th>").append("band-").append(j); if (units != null) { sb.append("(").append(units).append(")"); } sb.append("</th>"); } sb.append("</tr>"); header = sb.toString(); // navigation buttons nextAction = new AbstractAction(" > ") { @Override public void actionPerformed(ActionEvent e) { setCurrentTime(++currentTime); } }; previousAction = new AbstractAction(" < ") { @Override public void actionPerformed(ActionEvent e) { setCurrentTime(--currentTime); } }; final GridBagConstraints cst = new GridBagConstraints(); toolbar = new JToolBar(); toolbar.setLayout(new GridBagLayout()); toolbar.setFloatable(false); cst.gridx = 0; toolbar.add(new JButton(previousAction), cst); cst.gridx = 1; toolbar.add(new JButton(nextAction), cst); cst.gridx = 2; cst.weightx = 1; toolbar.add(label, cst); if (times.length > 0) { setCurrentTime(currentTime); } else { toolbar.setVisible(false); setCurrentTime(-1); } contentPane.add(toolbar, BorderLayout.SOUTH); this.add(contentPane, BorderLayout.CENTER); } private void setCurrentTime(int index) { textPane.setText(""); Double time = index != -1 ? times[index] : null; StringBuilder sb = new StringBuilder(header); for (Map.Entry<DirectPosition, double[]> entry : allValues.entrySet()) { final DirectPosition position = entry.getKey(); if (time == null || position.getOrdinate(timeIdx) == time) { sb.append("<tr>"); //crs dims for (int i = 0; i < dimIndexes.length; i++) { sb.append("<td>").append(position.getOrdinate(dimIndexes[i])).append("</td>"); } //bands double[] values = entry.getValue(); for (int i = 0; i < values.length; i++) { if (i == 0) { sb.append("<td class=\"separator\"></td>"); } sb.append("<td>").append(values[i]).append("</td>"); } sb.append("</tr>"); } } sb.append("</table>"); sb.append("</body></html>"); //update content textPane.setText(sb.toString()); //update time selectors if (index != -1) { previousAction.setEnabled(index != 0); nextAction.setEnabled(index < (times.length - 1)); if (temporalToJava != null) { try { final double[] value = new double[]{time}; temporalToJava.transform(value, 0, value, 0, 1); Date date = new Date(Double.valueOf(value[0]).longValue()); String dateStr = dateFormat.format(date); label.setText(" " + (currentTime + 1) + "/" + times.length + " : " + dateStr); } catch (TransformException e) { e.printStackTrace(); } } } //force size and repaint this.setSize(contentPane.getSize()); this.revalidate(); this.repaint(); } } private abstract static class Bridge extends AbstractGraphicVisitor { public static CoverageExtractor.Ray extractData(final ProjectedCoverage projectedCoverage, final RenderingContext2D context, final SearchAreaJ2D queryArea) throws TransformException, CoverageStoreException { return rayExtraction(projectedCoverage, context, queryArea); } } }