/* uDig - User Friendly Desktop Internet GIS client
* http://udig.refractions.net
* (C) 2010, Refractions Research Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD
* License v1.0 (http://udig.refractions.net/files/bsd3-v10.html).
*/
package org.locationtech.udig.catalog.internal.wmt.ui.view;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.locationtech.udig.catalog.IGeoResource;
import org.locationtech.udig.catalog.internal.wmt.WMTPlugin;
import org.locationtech.udig.catalog.internal.wmt.WMTRenderJob;
import org.locationtech.udig.catalog.internal.wmt.WMTScaleZoomLevelMatcher;
import org.locationtech.udig.catalog.internal.wmt.wmtsource.WMTSource;
import org.locationtech.udig.catalog.wmt.internal.Messages;
import org.locationtech.udig.project.ILayer;
import org.locationtech.udig.project.IMap;
import org.locationtech.udig.project.IMapCompositionListener;
import org.locationtech.udig.project.MapCompositionEvent;
import org.locationtech.udig.project.internal.Map;
import org.locationtech.udig.project.internal.commands.SetScaleCommand;
import org.locationtech.udig.project.render.IViewportModelListener;
import org.locationtech.udig.project.render.ViewportModelEvent;
import org.locationtech.udig.project.ui.ApplicationGIS;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.part.ViewPart;
import org.geotools.geometry.jts.ReferencedEnvelope;
public class WMTZoomLevelSwitcher extends ViewPart {
// Listeners
private ISelectionChangedListener listenerZoomLevel;
private IMapCompositionListener listenerMap;
private IViewportModelListener listenerViewport;
private IPartListener listenerMapEditor;
// GUI elements
private Composite parentControl;
private Button btnZoomOut;
private Button btnZoomIn;
private ComboViewer cvLayers;
private ComboViewer cvZoomLevels;
// Map/Layers/Zoom-Levels
private IMap currentMap = ApplicationGIS.NO_MAP;
private List<ILayer> layerList;
private Integer[] zoomLevels;
// Icons
private ImageRegistry imageCache;
private static final String ICONS_PATH = "/icons/etool16/"; //$NON-NLS-1$
private static final String ICON_ZOOM_IN = "ZOOM_IN"; //$NON-NLS-1$
private static final String ICON_ZOOM_IN_PATH = ICONS_PATH + "zoom_in_co.gif"; //$NON-NLS-1$
private static final String ICON_ZOOM_OUT = "ZOOM_OUT"; //$NON-NLS-1$
private static final String ICON_ZOOM_OUT_PATH = ICONS_PATH + "zoom_out_co.gif"; //$NON-NLS-1$
public WMTZoomLevelSwitcher() {
super();
initListeners();
}
//region Init Listeners
private void initListeners() {
initMapListener();
initViewportListener();
initMapEditorListener();
}
private void initMapListener() {
listenerMap = new IMapCompositionListener(){
public void changed(MapCompositionEvent event) {
if (parentControl == null || parentControl.isDisposed()) return;
parentControl.getDisplay().asyncExec(new Runnable(){
public void run() {
updateGUI(ApplicationGIS.getActiveMap());
}
});
}
};
}
private void initViewportListener() {
listenerViewport = new IViewportModelListener() {
public void changed(ViewportModelEvent event) {
if (parentControl == null || parentControl.isDisposed()) return;
// when the scale changes, update the zoom-level ComboBox
parentControl.getDisplay().asyncExec(new Runnable() {
public void run(){
updateGUIFromScale();
}
});
}
};
}
private void initMapEditorListener() {
listenerMapEditor = new IPartListener() {
private IWorkbenchPart currentPart;
public void partActivated(IWorkbenchPart part) {
if (part == currentPart)
return;
final IMap map = getMapFromPart(part);
if (map != null) {
currentPart = part;
if (parentControl == null || parentControl.isDisposed()) return;
parentControl.getDisplay().asyncExec(new Runnable() {
public void run(){
setUpMapListeners(map);
}
});
}
}
public void partBroughtToTop(IWorkbenchPart part) {
partActivated(part);
}
public void partClosed(IWorkbenchPart part) {
if (part == WMTZoomLevelSwitcher.this) {
if (parentControl == null || parentControl.isDisposed()) return;
// if the tool itself is closed
parentControl.getDisplay().asyncExec(new Runnable() {
public void run(){
removeAllListeners();
currentPart = null;
}
});
return;
}
if (part != currentPart)
return;
final IMap map = getMapFromPart(part);
if(map != null) {
if (parentControl == null || parentControl.isDisposed()) return;
// remove
parentControl.getDisplay().asyncExec(new Runnable() {
public void run(){
removeMapListeners(map, true);
}
});
}
currentPart = null;
}
private IMap getMapFromPart(IWorkbenchPart part) {
if (part != null) {
IAdaptable adaptable = (IAdaptable) part;
Object obj = adaptable.getAdapter(Map.class);
if (obj instanceof Map) {
return (Map) obj;
}
}
return null;
}
public void partDeactivated(IWorkbenchPart part) {}
public void partOpened(IWorkbenchPart part) {}
};
}
//endregion
//region Add/remove listeners
public void setUpMapListeners(IMap map) {
// remove listeners from old map
removeMapListeners(currentMap, false);
// assure that the listeners are only once in the list of the new map
removeMapListeners(map, false);
map.addMapCompositionListener(listenerMap);
map.getViewportModel().addViewportModelListener(listenerViewport);
updateGUI(map);
}
public void removeMapListeners(IMap map, boolean updateGUI) {
if (map == null || map == ApplicationGIS.NO_MAP) return;
map.removeMapCompositionListener(listenerMap);
map.getViewportModel().removeViewportModelListener(listenerViewport);
if (updateGUI && parentControl != null && !parentControl.isDisposed()) {
currentMap = ApplicationGIS.NO_MAP;
layerList.clear();
enableComponents(false);
}
}
public void removeAllListeners() {
removeMapListeners(currentMap, false);
getSite().getWorkbenchWindow().getPartService().removePartListener(listenerMapEditor);
}
//endregion
//region Create Control
@Override
public void createPartControl(final Composite parent) {
parentControl = parent;
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new RowLayout(SWT.HORIZONTAL));
//region Label "Layer"
Label lblLayer = new Label (composite, SWT.HORIZONTAL);
lblLayer.setText(Messages.ZoomLevelSwitcher_Layer);
//endregion
//region Layer ComboBox
cvLayers = new ComboViewer(composite, SWT.READ_ONLY);
cvLayers.setContentProvider(new ArrayContentProvider());
cvLayers.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
if (element instanceof ILayer) {
return ((ILayer) element).getName();
} else {
return super.getText(element);
}
}
});
cvLayers.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged( SelectionChangedEvent event ) {
updateZoomLevels();
updateGUIFromScale();
}
});
//endregion
//region Label "Zoom-Level"
Label lblZoomLevel = new Label (composite, SWT.HORIZONTAL);
lblZoomLevel.setText(Messages.ZoomLevelSwitcher_ZoomLevel);
//endregion
//region Zoom-Level ComboBox
cvZoomLevels = new ComboViewer(composite, SWT.READ_ONLY);
cvZoomLevels.setContentProvider(new ArrayContentProvider());
cvZoomLevels.setLabelProvider(new LabelProvider());
//endregion
//region Zoom-In/Zoom-Out Buttons
// load icons
setUpImageCache(parent);
btnZoomOut = new Button(composite, SWT.PUSH);
btnZoomOut.setImage(imageCache.get(ICON_ZOOM_OUT));
btnZoomOut.setToolTipText(Messages.ZoomLevelSwitcher_ZoomOut);
btnZoomOut.addSelectionListener(new SelectionListener() {
public void widgetSelected( SelectionEvent e ) {
zoomOut();
}
public void widgetDefaultSelected( SelectionEvent e ) {}
});
btnZoomIn = new Button(composite, SWT.PUSH);
btnZoomIn.setImage(imageCache.get(ICON_ZOOM_IN));
btnZoomIn.setToolTipText(Messages.ZoomLevelSwitcher_ZoomIn);
btnZoomIn.addSelectionListener(new SelectionListener() {
public void widgetSelected( SelectionEvent e ) {
zoomIn();
}
public void widgetDefaultSelected( SelectionEvent e ) {}
});
//endregion
//region Setup listeners
listenerZoomLevel = new ISelectionChangedListener() {
public void selectionChanged( SelectionChangedEvent event ) {
zoomToZoomLevel(getSelectedZoomLevel());
}
};
cvZoomLevels.addSelectionChangedListener(listenerZoomLevel);
setUpMapListeners(ApplicationGIS.getActiveMap());
getSite().getWorkbenchWindow().getPartService().addPartListener(listenerMapEditor);
//endregion
}
/**
* Creates the ImageRegistry and adds the two icons.
*/
private void setUpImageCache(final Composite parent) {
imageCache = new ImageRegistry(parent.getDisplay());
ImageDescriptor descZoomOut = ImageDescriptor.createFromFile(getClass(),
ICON_ZOOM_OUT_PATH);
imageCache.put(ICON_ZOOM_OUT, descZoomOut);
ImageDescriptor descZoomIn = ImageDescriptor.createFromFile(getClass(),
ICON_ZOOM_IN_PATH);
imageCache.put(ICON_ZOOM_IN, descZoomIn);
}
//endregion
//region GUI Helper Methods
//region Updates to the GUI when the map changed
private void updateGUI(IMap map) {
if (parentControl != null && !parentControl.isDisposed()) {
updateLayerList(map);
updateZoomLevels();
updateGUIFromScale();
parentControl.pack();
}
}
private void updateLayerList(IMap map) {
if (layerList == null) {
layerList = new ArrayList<ILayer>();
}
// remember the layer which is selected at the moment
ILayer selectedLayer = getSelectedLayer();
layerList.clear();
if (map == null || map == ApplicationGIS.NO_MAP) {
map = ApplicationGIS.getActiveMap();
}
if (map != ApplicationGIS.NO_MAP) {
List<ILayer> mapLayers = map.getMapLayers();
// look for layers which have WMTSource as georesource
for( ILayer layer : mapLayers ) {
if ((layer != null) && (layer.findGeoResource(WMTSource.class) != null)) {
// valid layer
layerList.add(layer);
}
}
}
cvLayers.setInput(layerList);
setSelectedLayer(selectedLayer);
enableComponents(!layerList.isEmpty());
// remember to which map these layers belong to
this.currentMap = map;
}
//endregion
//region Updates to the GUI when another layer was selected
private void updateZoomLevels() {
WMTSource wmtSource = getWMTSourceOfSelectedLayer();
if (wmtSource == null) {
cvZoomLevels.setInput(null);
} else {
int minZoomLevel = wmtSource.getMinZoomLevel();
int maxZoomLevel = wmtSource.getMaxZoomLevel();
generateZoomLevels(minZoomLevel, maxZoomLevel);
cvZoomLevels.setInput(zoomLevels);
}
}
//endregion
//region Updates to the GUI regarding the scale
private void updateGUIFromScale() {
try {
WMTSource wmtSource = getWMTSourceOfSelectedLayer();
if (wmtSource != null) {
// get the zoom-level for this scale
WMTScaleZoomLevelMatcher zoomLevelMatcher = getZoomLevelMatcher(wmtSource);
int zoomLevel = wmtSource.getZoomLevelFromMapScale(zoomLevelMatcher,
WMTRenderJob.getScaleFactor());
setSelectedZoomLevel(zoomLevel);
updateZoomButtons(zoomLevel);
} else {
throw new Exception("wmtSource is null"); //$NON-NLS-1$
}
} catch (Exception e) {
WMTPlugin.log("[WMTZoomLevelSwitcher.updateGUIFromScale] Failed ", e); //$NON-NLS-1$
}
}
//endregion
//region Methods for the zoom-level combo-box
private void setSelectedZoomLevel(int zoomLevel) {
List<Integer> selectedZoomLevels = new ArrayList<Integer>(1);
selectedZoomLevels.add(zoomLevel);
ISelection selection = new StructuredSelection(selectedZoomLevels);
cvZoomLevels.removeSelectionChangedListener(listenerZoomLevel);
cvZoomLevels.setSelection(selection);
cvZoomLevels.addSelectionChangedListener(listenerZoomLevel);
}
private int getSelectedZoomLevel() {
StructuredSelection selection = (StructuredSelection) cvZoomLevels.getSelection();
if (selection.isEmpty()) {
return zoomLevels[0];
} else {
return (Integer) selection.getFirstElement();
}
}
private void generateZoomLevels(int minZoomLevel, int maxZoomLevel) {
int length = maxZoomLevel - minZoomLevel + 1;
zoomLevels = new Integer[length];
int zoomLevel = minZoomLevel;
for (int i = 0; i < length; i++) {
zoomLevels[i] = zoomLevel++;
}
}
//endregion
//region Methods for the layer combo-box
private WMTSource getWMTSourceOfSelectedLayer() {
ILayer layer = getSelectedLayer();
if (layer == null) return null;
IGeoResource resource = layer.findGeoResource(WMTSource.class);
if (resource == null) return null;
try {
WMTSource wmtSource = resource.resolve(WMTSource.class, null);
return wmtSource;
} catch (IOException e) {
return null;
}
}
private ILayer getSelectedLayer() {
StructuredSelection selection = (StructuredSelection) cvLayers.getSelection();
if (selection.isEmpty()) {
return null;
} else {
return (ILayer) selection.getFirstElement();
}
}
private void setSelectedLayer(ILayer layer) {
if (layer == null || !layerList.contains(layer)) {
// try to get the first layer
if (layerList.isEmpty()) {
// no layer there to select
return;
} else {
layer = layerList.get(0);
}
}
// set the layer selected
List<ILayer> selectedLayers = new ArrayList<ILayer>(1);
selectedLayers.add(layer);
ISelection selection = new StructuredSelection(selectedLayers);
cvLayers.setSelection(selection);
}
//endregion
//region Methods to disable/enable GUI elements
private void enableComponents(boolean enabled) {
cvLayers.getCombo().setEnabled(enabled);
cvZoomLevels.getCombo().setEnabled(enabled);
btnZoomIn.setEnabled(enabled);
btnZoomOut.setEnabled(enabled);
}
private void updateZoomButtons(int zoomLevel) {
boolean zoomInEnabled = true;
boolean zoomOutEnabled = true;
if (zoomLevel <= zoomLevels[0]) {
zoomOutEnabled = false;
} else if (zoomLevel >= zoomLevels[zoomLevels.length-1]) {
zoomInEnabled = false;
}
btnZoomIn.setEnabled(zoomInEnabled);
btnZoomOut.setEnabled(zoomOutEnabled);
}
//endregion
//region Methods to zoom in/out
private synchronized WMTScaleZoomLevelMatcher getZoomLevelMatcher(WMTSource wmtSource) throws Exception {
double mapScale = currentMap.getViewportModel().getScaleDenominator();
ReferencedEnvelope mapExtentMapCrs = currentMap.getViewportModel().getBounds();
return WMTScaleZoomLevelMatcher.createMatcher(
mapExtentMapCrs, mapScale, wmtSource);
}
private void zoomIn() {
int zoomLevel = getSelectedZoomLevel();
if (zoomLevel < zoomLevels[zoomLevels.length-1]){
zoomToZoomLevel(zoomLevel+1);
}
}
private void zoomOut() {
int zoomLevel = getSelectedZoomLevel();
if (zoomLevel > zoomLevels[0]){
zoomToZoomLevel(zoomLevel-1);
}
}
private void zoomToZoomLevel(int zoomLevel) {
try {
WMTSource wmtSource = getWMTSourceOfSelectedLayer();
if (wmtSource != null) {
WMTScaleZoomLevelMatcher zoomLevelMatcher = getZoomLevelMatcher(wmtSource);
double scale = zoomLevelMatcher.getOptimumScaleFromZoomLevel(zoomLevel,
wmtSource);
zoomToScale(scale);
} else {
throw new Exception("wmtSource is null"); //$NON-NLS-1$
}
} catch (Exception e) {
WMTPlugin.log("[WMTZoomLevelSwitcher.zoomToZoomLevel] Zooming failed: " + zoomLevel, e); //$NON-NLS-1$
}
}
private void zoomToScale(double scale) {
ApplicationGIS.getActiveMap().sendCommandASync(new SetScaleCommand(scale));
}
//endregion
//endregion
@Override
public void setFocus() {}
}