/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2013, 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.render3d.control;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import javax.measure.IncommensurableException;
import javax.swing.DefaultListCellRenderer;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.LayoutStyle;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.storage.DataStoreException;
import org.geotoolkit.storage.coverage.CoverageReference;
import org.geotoolkit.storage.coverage.Pyramid;
import org.geotoolkit.storage.coverage.PyramidalCoverageReference;
import org.geotoolkit.display.PortrayalException;
import org.geotoolkit.display3d.Map3D;
import org.geotoolkit.display3d.scene.ContextContainer3D;
import org.geotoolkit.display3d.scene.Terrain;
import org.geotoolkit.display3d.scene.camera.TrackBallCamera;
import org.geotoolkit.display3d.scene.loader.DefaultElevationLoader;
import org.geotoolkit.display3d.scene.loader.ElevationLoader;
import org.geotoolkit.display3d.scene.loader.FlatElevationLoader;
import org.geotoolkit.display3d.scene.loader.ImageLoader;
import org.geotoolkit.display3d.scene.loader.MapContextImageLoader;
import org.geotoolkit.display3d.scene.loader.PyramidImageLoader;
import org.geotoolkit.display3d.scene.loader.StackElevationLoader;
import org.geotoolkit.gui.swing.render3d.JMap3D;
import org.geotoolkit.gui.swing.resource.MessageBundle;
import org.geotoolkit.map.ContextListener;
import org.geotoolkit.map.CoverageMapLayer;
import org.geotoolkit.map.MapBuilder;
import org.geotoolkit.map.MapContext;
import org.geotoolkit.map.MapItem;
import org.geotoolkit.map.MapLayer;
import org.apache.sis.referencing.CommonCRS;
import org.geotoolkit.util.collection.CollectionChangeEvent;
import org.jdesktop.swingx.combobox.ListComboBoxModel;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.FactoryException;
/**
*
* @author Johann Sorel (Geomatys)
*/
public class JTerrainConfigPanel extends javax.swing.JPanel {
private final JMap3D map;
private final MapContext context;
/**
* Creates new form JTerrainConfigPanel
* @param map3d
*/
public JTerrainConfigPanel(final JMap3D map3d) {
initComponents();
this.map = map3d;
this.context = ((ContextContainer3D)map3d.getMap3D().getContainer()).getContext();
guiApply.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
resetScene(
guiLayerList.getSelectedValuesList(),
guiMntList.getSelectedValuesList());
resetCamera();
}
});
updateLayersBox();
//listen to any change in the map context to update combo boxes
context.addContextListener(new ContextListener() {
@Override
public void layerChange(CollectionChangeEvent<MapLayer> event) {
updateLayersBox();
}
@Override
public void itemChange(CollectionChangeEvent<MapItem> event) {}
@Override
public void propertyChange(PropertyChangeEvent evt) {}
});
//listen to selection change on combo box to validate/unvalidate apply button
final ListSelectionListener listener = new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
guiApply.setEnabled(guiLayerList.getSelectedValue()!= null && guiMntList.getSelectedValue() != null);
}
};
guiLayerList.addListSelectionListener(listener);
guiMntList.addListSelectionListener(listener);
guiLayerList.setCellRenderer(new CoverageRefRenderer());
guiMntList.setCellRenderer(new CoverageRefRenderer());
//extract selected values from current map
// final ContextContainer3D container = (ContextContainer3D) map.getMap3D().getContainer();
// final Terrain terrain = container.getTerrain();
// if(terrain != null){
// final ImageLoader imgLoader = terrain.getImageLoader();
// final ElevationLoader eleLoader = terrain.getElevationLoader();
//
// if(imgLoader instanceof PyramidImageLoader){
// final PyramidalCoverageReference ref = ((PyramidImageLoader)imgLoader).getCoverageReference();
// guiLayerList.setSelectedValue(ref, true);
// }else if(imgLoader instanceof MapContextImageLoader){
// final List<MapLayer> layers = ((MapContextImageLoader)imgLoader).getContext().layers();
// }
//
// }
}
private void updateLayersBox(){
final List<MapItem> lst = new ArrayList<>(context.items());
Collections.reverse(lst);
final List<MapItem> validImageLayers = new ArrayList<>();
final List<CoverageReference> validMNTLayers = new ArrayList<>();
for(MapItem l : lst){
validImageLayers.add(l);
if(l instanceof CoverageMapLayer){
final CoverageReference ref = ((CoverageMapLayer)l).getCoverageReference();
validMNTLayers.add(ref);
}
}
guiLayerList.setModel(new ListComboBoxModel(validImageLayers));
guiLayerList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
if(validImageLayers.size()==1){
guiLayerList.setSelectedIndex(0);
}
guiMntList.setModel(new ListComboBoxModel(validMNTLayers));
guiMntList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
if(validMNTLayers.size()==1){
guiMntList.setSelectedIndex(0);
}
guiApply.setEnabled(guiLayerList.getSelectedValue()!= null && guiMntList.getSelectedValue() != null);
}
private void resetScene(final List<MapItem> contextLayers, final List<CoverageReference> mntList){
try{
GeneralEnvelope envelope = null;
int numMosaic = 1;
final ElevationLoader elevationLoader;
if(mntList.size()==1){
//single elevation
elevationLoader = new DefaultElevationLoader(mntList.get(0));
}else if( mntList.size()>1){
//multiple elevation
final List<ElevationLoader> mnts = new ArrayList<>();
for(CoverageReference ref : mntList){
mnts.add(new DefaultElevationLoader(ref));
}
elevationLoader = new StackElevationLoader(mnts);
}else{
//no elevation
elevationLoader = new FlatElevationLoader(0);
}
//prepare the terrain image loader
ImageLoader imageLoader = null;
imagepyramid:
if(contextLayers.size() == 1){
final MapItem layer = contextLayers.get(0);
if(layer instanceof CoverageMapLayer){
//we can use the coverage reference directly
final CoverageReference modelImg = ((CoverageMapLayer)layer).getCoverageReference();
if(modelImg instanceof PyramidalCoverageReference){
final PyramidalCoverageReference pcf = (PyramidalCoverageReference) modelImg;
final List<Pyramid> pyramidsImg = (List<Pyramid>) pcf.getPyramidSet().getPyramids();
Pyramid pyramidImg = null;
if (pyramidsImg.size() > 0){
pyramidImg = pyramidsImg.get(0);
}
imageLoader = new PyramidImageLoader(pcf, pyramidImg);
}
}
}
if(imageLoader==null){
//use a map context image loader
final MapContext context = MapBuilder.createContext();
context.items().addAll(contextLayers);
Collections.reverse(context.items());
imageLoader = new MapContextImageLoader(context);
}
if(envelope == null) {
GeneralEnvelope env = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
env.setRange(0, -180.0, 180.0);
env.setRange(1, -90.0, 90.0);
envelope = env;
numMosaic = 21;
}
final Terrain terrain = ((ContextContainer3D)map.getMap3D().getContainer()).createTerrain(envelope, numMosaic);
terrain.setElevationLoader(elevationLoader);
terrain.setImageLoader(imageLoader);
} catch (PortrayalException | TransformException | FactoryException | DataStoreException | IncommensurableException ex) {
Map3D.LOGGER.log(Level.WARNING, ex.getMessage(), ex);
}
}
private void resetCamera(){
final Map3D map3d = map.getMap3D();
final Terrain terrain = ((ContextContainer3D)map3d.getContainer()).getTerrain();
if (terrain != null){
final TrackBallCamera camera = map3d.getCamera();
final float x = (float) terrain.getEnvelope().getMedian(0);
final float y = (float) terrain.getEnvelope().getMedian(1);
final double maxScale = terrain.getEnvelope().getSpan(0) / camera.getWidth();
final float maxLength = (float)map3d.getDistForScale(maxScale);
camera.setCenter(x, y, 0.0f);
camera.zoomTo(maxLength);
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jPanel1 = new JPanel();
jLabel1 = new JLabel();
jScrollPane1 = new JScrollPane();
guiMntList = new JList();
jPanel2 = new JPanel();
jLabel2 = new JLabel();
jScrollPane2 = new JScrollPane();
guiLayerList = new JList();
guiApply = new JButton();
jLabel1.setHorizontalAlignment(SwingConstants.CENTER);
jLabel1.setText(MessageBundle.format("org_geotoolkit_gui_swing_render3d_mnt")); // NOI18N
jScrollPane1.setViewportView(guiMntList);
GroupLayout jPanel1Layout = new GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(jLabel1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jScrollPane1, GroupLayout.DEFAULT_SIZE, 194, Short.MAX_VALUE)
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
);
jLabel2.setHorizontalAlignment(SwingConstants.CENTER);
jLabel2.setText(MessageBundle.format("org_geotoolkit_gui_swing_render3d_layer")); // NOI18N
jScrollPane2.setViewportView(guiLayerList);
GroupLayout jPanel2Layout = new GroupLayout(jPanel2);
jPanel2.setLayout(jPanel2Layout);
jPanel2Layout.setHorizontalGroup(
jPanel2Layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(jLabel2, GroupLayout.DEFAULT_SIZE, 196, Short.MAX_VALUE)
.addComponent(jScrollPane2, GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
);
jPanel2Layout.setVerticalGroup(
jPanel2Layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addComponent(jLabel2)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane2, GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
);
guiApply.setText(MessageBundle.format("apply")); // NOI18N
GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jPanel1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jPanel2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addComponent(guiApply)
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(jPanel1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jPanel2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(39, Short.MAX_VALUE)
.addComponent(guiApply)
.addGap(33, 33, 33))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private JButton guiApply;
private JList guiLayerList;
private JList guiMntList;
private JLabel jLabel1;
private JLabel jLabel2;
private JPanel jPanel1;
private JPanel jPanel2;
private JScrollPane jScrollPane1;
private JScrollPane jScrollPane2;
// End of variables declaration//GEN-END:variables
private static class CoverageRefRenderer extends DefaultListCellRenderer{
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
final JLabel lbl = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if(value instanceof CoverageReference){
final CoverageReference ref = (CoverageReference) value;
lbl.setText(ref.getName().tip().toString());
}else if(value instanceof MapItem){
final MapItem mapitem = (MapItem) value;
String txt = mapitem.getName();
if( (txt==null || txt.isEmpty()) && mapitem.getDescription() != null){
txt = String.valueOf(mapitem.getDescription().getTitle());
}
if( (txt==null || txt.isEmpty())){
txt = "";
}
lbl.setText(txt);
}
return lbl;
}
}
}