/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * 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 2 * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ package com.vividsolutions.jump.workbench.ui.zoom; import java.awt.*; import java.awt.event.*; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.*; import java.util.List; import javax.swing.*; import javax.swing.Timer; import javax.swing.plaf.basic.BasicSliderUI; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.util.Assert; import com.vividsolutions.jump.I18N; import com.vividsolutions.jump.feature.Feature; import com.vividsolutions.jump.feature.FeatureUtil; import com.vividsolutions.jump.geom.EnvelopeUtil; import com.vividsolutions.jump.geom.LineSegmentEnvelopeIntersector; import com.vividsolutions.jump.util.Blackboard; import com.vividsolutions.jump.util.CoordinateArrays; import com.vividsolutions.jump.util.MathUtil; import com.vividsolutions.jump.workbench.model.CategoryEvent; import com.vividsolutions.jump.workbench.model.FeatureEvent; import com.vividsolutions.jump.workbench.model.FeatureEventType; import com.vividsolutions.jump.workbench.model.Layer; import com.vividsolutions.jump.workbench.model.LayerEvent; import com.vividsolutions.jump.workbench.model.LayerEventType; import com.vividsolutions.jump.workbench.model.LayerListener; import com.vividsolutions.jump.workbench.model.LayerManager; import com.vividsolutions.jump.workbench.ui.GUIUtil; import com.vividsolutions.jump.workbench.ui.LayerViewPanel; import com.vividsolutions.jump.workbench.ui.LayerViewPanelContext; import com.vividsolutions.jump.workbench.ui.LayerViewPanelProxy; import com.vividsolutions.jump.workbench.ui.Viewport; import com.vividsolutions.jump.workbench.ui.ViewportListener; import com.vividsolutions.jump.workbench.ui.WorkbenchFrame; import com.vividsolutions.jump.workbench.ui.plugin.scalebar.IncrementChooser; import com.vividsolutions.jump.workbench.ui.plugin.scalebar.MetricSystem; import com.vividsolutions.jump.workbench.ui.plugin.scalebar.RoundQuantity; import com.vividsolutions.jump.workbench.ui.plugin.scalebar.ScaleBarRenderer; import com.vividsolutions.jump.workbench.ui.renderer.java2D.Java2DConverter; public class ZoomBar extends JPanel implements Java2DConverter.PointConverter { private int totalGeometries() { int totalGeometries = 0; // Restrict count to visible layers [mmichaud 2007-05-27] for (Iterator i = layerViewPanel().getLayerManager().getVisibleLayers(true).iterator(); i.hasNext();) { Layer layer = (Layer) i.next(); totalGeometries += layer.getFeatureCollectionWrapper().size(); } return totalGeometries; } private Envelope lastGoodEnvelope = null; private WorkbenchFrame frame; private BorderLayout borderLayout1 = new BorderLayout(); private JSlider slider = new JSlider(); private JLabel label = new JLabel(); private IncrementChooser incrementChooser = new IncrementChooser(); private Collection metricUnits = new MetricSystem(1).createUnits(); // Add java2DConverter and affineTransform for coordinate decimation private Java2DConverter java2DConverter; private AffineTransform affineTransform; public ZoomBar( boolean showingSliderLabels, boolean showingRightSideLabel, WorkbenchFrame frame) throws NoninvertibleTransformException { this.frame = frame; this.showingSliderLabels = showingSliderLabels; slider.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { try { updateComponents(); } catch (NoninvertibleTransformException x) { //Eat it. [Jon Aquino] } } }); if (showingSliderLabels) { //Add a dummy label so that ZoomBars added to Toolboxes are //packed properly. [Jon Aquino] Hashtable labelTable = new Hashtable(); labelTable.put(new Integer(0), new JLabel(" ")); slider.setLabelTable(labelTable); } try { jbInit(); } catch (Exception ex) { ex.printStackTrace(); } if (!showingRightSideLabel) { remove(label); } label.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 3 && SwingUtilities.isRightMouseButton(e)) { viewBlackboard().put(USER_DEFINED_MIN_SCALE, null); viewBlackboard().put(USER_DEFINED_MAX_SCALE, null); clearModelCaches(); } } }); slider.addMouseMotionListener(new MouseMotionAdapter() { //Use #mouseDragged rather than JSlider#stateChanged because we //are interested in user-initiated slider changes, not programmatic //slider changes. [Jon Aquino] public void mouseDragged(MouseEvent e) { try { layerViewPanel().erase((Graphics2D) layerViewPanel().getGraphics()); drawWireframe(); ScaleBarRenderer scaleBarRenderer = (ScaleBarRenderer) layerViewPanel().getRenderingManager().getRenderer( ScaleBarRenderer.CONTENT_ID); if (scaleBarRenderer != null) { scaleBarRenderer.paint( (Graphics2D) layerViewPanel().getGraphics(), getScale()); } updateLabel(); } catch (NoninvertibleTransformException x) { //Eat it. [Jon Aquino] } } }); if (slider.getUI() instanceof BasicSliderUI) { slider.addMouseMotionListener(new MouseMotionAdapter() { public void mouseMoved(MouseEvent e) { if (layerViewPanel() == dummyLayerViewPanel) { return; } try { slider.setToolTipText( I18N.get("ui.zoom.ZoomBar.zoom-to")+" " + chooseGoodIncrement( toScale( ((BasicSliderUI) slider.getUI()).valueForXPosition( e.getX()))) .toString()); } catch (NoninvertibleTransformException x) { slider.setToolTipText(I18N.get("ui.zoom.ZoomBar.zoom")); } } }); } label.setPreferredSize(new Dimension(50, label.getHeight())); slider.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent e) { try { if (e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_RIGHT) { gestureFinished(); } } catch (NoninvertibleTransformException t) { layerViewPanel().getContext().handleThrowable(t); } } }); slider.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if (!slider.isEnabled()) { return; } layerViewPanel().getRenderingManager().setPaintingEnabled(false); } public void mouseReleased(MouseEvent e) { try { gestureFinished(); } catch (NoninvertibleTransformException t) { layerViewPanel().getContext().handleThrowable(t); } } }); // // Whenever anything happens on an internal frame we want to do this. // GUIUtil.addInternalFrameListener( frame.getDesktopPane(), GUIUtil.toInternalFrameListener(new ActionListener() { public void actionPerformed(ActionEvent e) { installListenersOnCurrentPanel(); try { updateComponents(); } catch (NoninvertibleTransformException x) { //Eat it. [Jon Aquino] } } })); // added to use the decimator implemented in Java2DConverter [mmichaud 2007-05-27] java2DConverter = new Java2DConverter(this, 2); installListenersOnCurrentPanel(); updateComponents(); } private void installListenersOnCurrentPanel() { installViewListeners(); installModelListeners(); } private void installViewListeners() { //Use hash code to uniquely identify this zoom bar (there may be other //zoom bars) [Jon Aquino] String VIEW_LISTENERS_INSTALLED_KEY = Integer.toHexString(hashCode()) + " - VIEW LISTENERS INSTALLED"; if (viewBlackboard().get(VIEW_LISTENERS_INSTALLED_KEY) != null) { return; } if ( layerViewPanel() == null ){ return; } layerViewPanel().getViewport().addListener(new ViewportListener() { public void zoomChanged(Envelope modelEnvelope) { if (!viewBlackboard().get(CENTRE_LOCKED_KEY, false)) { viewBlackboard().put(CENTRE_KEY, null); } viewBlackboard().put(SCALE_KEY, null); try { if (layerViewPanel().getViewport().getScale() < getMinScale()) { viewBlackboard().put( USER_DEFINED_MIN_SCALE, layerViewPanel().getViewport().getScale()); } if (layerViewPanel().getViewport().getScale() > getMaxScale()) { viewBlackboard().put( USER_DEFINED_MAX_SCALE, layerViewPanel().getViewport().getScale()); } updateComponents(); } catch (NoninvertibleTransformException e) { //Eat it. [Jon Aquino] } } }); viewBlackboard().put(VIEW_LISTENERS_INSTALLED_KEY, new Object()); } private void installModelListeners() { //Use hash code to uniquely identify this zoom bar (there may be other //zoom bars) [Jon Aquino] String MODEL_LISTENERS_INSTALLED_KEY = Integer.toHexString(hashCode()) + " - MODEL LISTENERS INSTALLED"; if (viewBlackboard().get(MODEL_LISTENERS_INSTALLED_KEY) != null) { return; } if ( layerViewPanel() == null ){ return; } layerViewPanel().getLayerManager().addLayerListener(new LayerListener() { public void categoryChanged(CategoryEvent e) {} public void featuresChanged(FeatureEvent e) { if (e.getType() == FeatureEventType.ADDED || e.getType() == FeatureEventType.DELETED || e.getType() == FeatureEventType.GEOMETRY_MODIFIED) { clearModelCaches(); } } // add LayerEventType.VISIBILITY_CHANGED condition [mmichaud 2007-05-27] public void layerChanged(LayerEvent e) { if (e.getType() == LayerEventType.ADDED || e.getType() == LayerEventType.REMOVED || e.getType() == LayerEventType.VISIBILITY_CHANGED) { clearModelCaches(); } } }); viewBlackboard().put(MODEL_LISTENERS_INSTALLED_KEY, new Object()); } private void queueComponentUpdate() { componentUpdateTimer.restart(); } /** Coalesces component updates */ private Timer componentUpdateTimer = GUIUtil.createRestartableSingleEventTimer(200, new ActionListener() { public void actionPerformed(ActionEvent e) { try { updateComponents(); } catch (NoninvertibleTransformException x) { //Eat it. [Jon Aquino] } } }); public void updateComponents() throws NoninvertibleTransformException { LayerViewPanel layerViewPanel = layerViewPanel(); if (layerViewPanel == dummyLayerViewPanel || layerViewPanel == null) { setComponentsEnabled(false); return; } setComponentsEnabled(true); //Must set slider value *before* updating the label on the right. [Jon Aquino] //I'm currently hiding the label on the right, to save real estate. [Jon Aquino] slider.setValue( toSliderValue( viewBlackboard().get(SCALE_KEY, layerViewPanel.getViewport().getScale()))); updateLabel(); updateSliderLabels(); } private void gestureFinished() throws NoninvertibleTransformException { if (!slider.isEnabled()) { return; } try { viewBlackboard().put(CENTRE_LOCKED_KEY, true); try { layerViewPanel().getViewport().zoom(proposedModelEnvelope()); } finally { viewBlackboard().put(CENTRE_LOCKED_KEY, false); } } finally { layerViewPanel().getRenderingManager().setPaintingEnabled(true); } } private Envelope proposedModelEnvelope() throws NoninvertibleTransformException { Coordinate centre = (Coordinate) viewBlackboard().get( CENTRE_KEY, EnvelopeUtil.centre( layerViewPanel().getViewport().getEnvelopeInModelCoordinates())); double width = layerViewPanel().getWidth() / getScale(); double height = layerViewPanel().getHeight() / getScale(); Envelope proposedModelEnvelope = new Envelope( centre.x - (width / 2), centre.x + (width / 2), centre.y - (height / 2), centre.y + (height / 2)); if (proposedModelEnvelope.getWidth() == 0 || proposedModelEnvelope.getHeight() == 0) { //We're zoomed waaay out! Avoid infinite scale. [Jon Aquino] proposedModelEnvelope = lastGoodEnvelope; } else { lastGoodEnvelope = proposedModelEnvelope; } return proposedModelEnvelope; } /** * Return the scale of the view according to the zoom bar */ //getScale() public to implement Java2DConverter.PointConverter [mmichaud 2007-05-26] public double getScale() throws NoninvertibleTransformException { return toScale(slider.getValue()); } private Stroke stroke = new BasicStroke(1); private void drawWireframe() throws NoninvertibleTransformException { Graphics2D g = (Graphics2D) layerViewPanel().getGraphics(); g.setColor(Color.lightGray); g.setStroke(stroke); g.draw(getWireFrame()); } private static final String SEGMENT_CACHE_KEY = ZoomBar.class.getName() + " - SEGMENT CACHE"; private void clearModelCaches() { //Use LayerManager blackboard for segment cache, so that multiple //views can share it. [Jon Aquino] modelBlackboard().put(SEGMENT_CACHE_KEY, null); modelBlackboard().put(MIN_EXTENT_KEY, null); modelBlackboard().put(MAX_EXTENT_KEY, null); //It's expensive to recompute these cached values, so queue the call //to #updateComponents [Jon Aquino] queueComponentUpdate(); } private LineSegmentEnvelopeIntersector lineSegmentEnvelopeIntersector = new LineSegmentEnvelopeIntersector(); // Modified by [mmichaud 2007-05-26] to use decimation algorithm from Java2DConverter private Shape getWireFrame() throws NoninvertibleTransformException { // affineTransform computed according to the zoombar slider (getScale) affineTransform = Viewport.modelToViewTransform( getScale(), new Point2D.Double( proposedModelEnvelope().getMinX(), proposedModelEnvelope().getMinY()), layerViewPanel().getSize().getHeight()); // view2D rectangle Rectangle2D view2D = new Rectangle2D.Double( 0.0, 0.0, layerViewPanel().getWidth(), layerViewPanel().getWidth()); GeneralPath wireFrame = new GeneralPath(); ArrayList segments = new ArrayList(getSegmentCache()); //segments.addAll(toSegments(randomOnScreenGeometries())); segments.addAll(toSegments(largeOnScreenGeometries())); for (Iterator i = segments.iterator(); i.hasNext();) { Coordinate[] coordinates = java2DConverter.toViewCoordinates((Coordinate[]) i.next()); boolean drawing = false; for (int j = 1; j < coordinates.length; j++) { if (!view2D.intersectsLine(coordinates[j - 1].x, coordinates[j - 1].y, coordinates[j].x, coordinates[j].y)) { drawing = false; continue; } if (!drawing) { wireFrame.moveTo((float) coordinates[j - 1].x, (float) coordinates[j - 1].y); } wireFrame.lineTo((float) coordinates[j].x, (float) coordinates[j].y); drawing = true; } } return wireFrame; } private Collection getSegmentCache() throws NoninvertibleTransformException { //Use LayerManager blackboard for segment cache, so that multiple //views can share it. [Jon Aquino] if (modelBlackboard().get(SEGMENT_CACHE_KEY) == null) { //modelBlackboard().put(SEGMENT_CACHE_KEY, toSegments(randomGeometries())); modelBlackboard().put(SEGMENT_CACHE_KEY, toSegments(largeGeometries())); // // We only want to do this when a frame closes. If we don't clear the // cache in the blackboard then we'll get a memory leak. // frame.getActiveInternalFrame().addInternalFrameListener( new InternalFrameAdapter() { public void internalFrameClosing(InternalFrameEvent e) { modelBlackboard().put(SEGMENT_CACHE_KEY, null); } }); } return (Collection) modelBlackboard().get(SEGMENT_CACHE_KEY); } private Collection toSegments(Collection geometries) { ArrayList segments = new ArrayList(); for (Iterator i = geometries.iterator(); i.hasNext();) { Geometry geometry = (Geometry) i.next(); segments.addAll(CoordinateArrays.toCoordinateArrays(geometry, false)); } return segments; } // Replace RANDOM_ONSCREEN_GEOMETRIES by LARGE_ONSCREEN_GEOMETRIES // private static final int RANDOM_ONSCREEN_GEOMETRIES = 100; // private static final int RANDOM_GEOMETRIES = 100; private static final int LARGE_GEOMETRIES = 100; private static final int LARGE_ONSCREEN_GEOMETRIES = 200; // start [mmichaud] // additional code to replace randomGeometries by a largestGeometries approach // one bad side effect of random geometries approach was visible for a big set of points // and a small set of polygons : polygons were not visible because of the proportional // selection of geometries, and points are not displayed :-( static final Comparator MAX_SIZE_COMPARATOR = new Comparator() { public int compare(Object f1, Object f2) { Envelope env1 = ((Feature)f1).getGeometry().getEnvelopeInternal(); Envelope env2 = ((Feature)f2).getGeometry().getEnvelopeInternal(); double size1 = Math.max(env1.getWidth(), env1.getHeight()); double size2 = Math.max(env2.getWidth(), env2.getHeight()); return size1 < size2 ? 1 : (size1 > size2 ? -1 : 0); } }; private Collection largeGeometries(int maxSize, List features) { Collections.sort(features, MAX_SIZE_COMPARATOR); List geometries = new ArrayList(); for (int i = 0 , max = Math.min(maxSize, features.size()) ; i < max ; i++) { geometries.add(((Feature)features.get(i)).getGeometry()); } //System.out.println("" + geometries.size() + "/" + features.size()); return geometries; } private Collection largeOnScreenGeometries() { List onScreenFeatures = new ArrayList(); if (totalGeometries() == 0) { return onScreenFeatures; } // Use proposedModelEnvelope (dynamically computed while the mouse is dragged) // instead of layerViewPanel().getViewport().getEnvelopeInModelCoordinates() // [mmichaud 2007-05-27] Envelope modelEnvelope; try { modelEnvelope = proposedModelEnvelope(); } catch(NoninvertibleTransformException e) { modelEnvelope = layerViewPanel().getViewport().getEnvelopeInModelCoordinates(); } // Restrict to visible layers [mmichaud 2007-05-27] for (Iterator it = layerViewPanel().getLayerManager() .getVisibleLayers(true) .iterator() ; it.hasNext() ; ) { Layer layer = (Layer) it.next(); // Select features intersecting the window List visibleFeatures = layer.getFeatureCollectionWrapper().query(modelEnvelope); if (visibleFeatures.size() < 1000) { onScreenFeatures.addAll(layer.getFeatureCollectionWrapper().query(modelEnvelope)); } // If there are more than 1000 visible features in this layer // select a maximum of 2000 features stepping through the list else { int step = visibleFeatures.size()/1000; for (int i = 0 , max = visibleFeatures.size() ; i < max ; i += step) { onScreenFeatures.add(visibleFeatures.get(i)); } } } return largeGeometries(LARGE_ONSCREEN_GEOMETRIES, onScreenFeatures); } private Collection largeGeometries() { ArrayList largeGeometries = new ArrayList(); if (totalGeometries() == 0) { return largeGeometries; } Envelope modelEnvelope = layerViewPanel().getViewport().getEnvelopeInModelCoordinates(); for (Iterator i = layerViewPanel().getLayerManager().getVisibleLayers(true).iterator(); i.hasNext();) { Layer layer = (Layer) i.next(); List visibleFeatures = layer.getFeatureCollectionWrapper().query(modelEnvelope); largeGeometries.addAll(layer.getFeatureCollectionWrapper().getFeatures()); } return largeGeometries(LARGE_GEOMETRIES, largeGeometries); } // end [mmichaud] // Replaced Random geometries by large geometries /* private Collection randomGeometries(int maxSize, List features) { if (features.size() <= maxSize) { return FeatureUtil.toGeometries(features); } ArrayList randomGeometries = new ArrayList(); for (int j = 0; j < maxSize; j++) { randomGeometries.add( ((Feature) features.get((int) (Math.random() * features.size()))).getGeometry()); } return randomGeometries; } private Collection randomOnScreenGeometries() { ArrayList randomOnScreenGeometries = new ArrayList(); // Avoid method computation inside the loop [mmichaud] int totalGeometries = totalGeometries(); if (totalGeometries == 0) { return randomOnScreenGeometries; } // Use proposedModelEnvelope (dynamically computed while the mouse is dragged) // instead of layerViewPanel().getViewport().getEnvelopeInModelCoordinates() // [mmichaud 2007-05-27] Envelope modelEnvelope; try { modelEnvelope = proposedModelEnvelope(); } catch(NoninvertibleTransformException e) { modelEnvelope = layerViewPanel().getViewport().getEnvelopeInModelCoordinates(); } // Restrict to visible layers [mmichaud 2007-05-27] for (Iterator i = layerViewPanel().getLayerManager().getVisibleLayers(true).iterator(); i.hasNext();) { Layer layer = (Layer) i.next(); randomOnScreenGeometries.addAll( randomGeometries( RANDOM_ONSCREEN_GEOMETRIES * layer.getFeatureCollectionWrapper().size() / totalGeometries(), layer.getFeatureCollectionWrapper().query( //layerViewPanel().getViewport().getEnvelopeInModelCoordinates()))); modelEnvelope))); } return randomOnScreenGeometries; } private Collection randomGeometries() { ArrayList randomGeometries = new ArrayList(); if (totalGeometries() == 0) { return randomGeometries; } // Restrict to visible layers [mmichaud 2007-05-27] for (Iterator i = layerViewPanel().getLayerManager().getVisibleLayers(true).iterator(); i.hasNext();) { Layer layer = (Layer) i.next(); randomGeometries.addAll( randomGeometries( RANDOM_GEOMETRIES * layer.getFeatureCollectionWrapper().size() / totalGeometries(), layer.getFeatureCollectionWrapper().getFeatures())); } return randomGeometries; } */ private int toSliderValue(double scale) throws NoninvertibleTransformException { return slider.getMaximum() - (int) (slider.getMaximum() * (MathUtil.base10Log(scale) - MathUtil.base10Log(getMinScale())) / (MathUtil.base10Log(getMaxScale()) - MathUtil.base10Log(getMinScale()))); } private double getMinExtent() throws NoninvertibleTransformException { if (modelBlackboard().get(MIN_EXTENT_KEY) == null) { double smallSegmentLength = chooseSmallSegmentLength(getSegmentCache()); //-1 smallSegmentLength means there is no data or the data are all //points (i.e. no segments). [Jon Aquino] if (smallSegmentLength == -1) { return -1; } modelBlackboard().put(MIN_EXTENT_KEY, smallSegmentLength); } Assert.isTrue(modelBlackboard().getDouble(MIN_EXTENT_KEY) > 0); return modelBlackboard().getDouble(MIN_EXTENT_KEY); } private double chooseSmallSegmentLength(Collection segmentCache) { int segmentsChecked = 0; double smallSegmentLength = -1; for (Iterator i = segmentCache.iterator(); i.hasNext();) { Coordinate[] coordinates = (Coordinate[]) i.next(); for (int j = 1; j < coordinates.length; j++) { double segmentLength = coordinates[j].distance(coordinates[j - 1]); segmentsChecked++; if (segmentLength > 0 && (smallSegmentLength == -1 || segmentLength < smallSegmentLength)) { smallSegmentLength = segmentLength; } if (segmentsChecked > 100) { break; } } if (segmentsChecked > 100) { break; } } return smallSegmentLength; } private double getMaxExtent() throws NoninvertibleTransformException { if (modelBlackboard().get(MAX_EXTENT_KEY) == null) { if (getSegmentCache().isEmpty()) { return -1; } modelBlackboard().put( MAX_EXTENT_KEY, layerViewPanel().getLayerManager().getEnvelopeOfAllLayers().getWidth()); } return modelBlackboard().getDouble(MAX_EXTENT_KEY); } private double getMaxScale() throws NoninvertibleTransformException { double maxScale = (getMinExtent() == -1 || getMinExtent() == 0) ? 1E3 : (1000 * layerViewPanel().getWidth() / getMinExtent()); if (viewBlackboard().get(USER_DEFINED_MAX_SCALE) != null) { return Math.max(maxScale, viewBlackboard().getDouble(USER_DEFINED_MAX_SCALE)); } return maxScale; } private double getMinScale() throws NoninvertibleTransformException { double minScale = (getMaxExtent() == -1 || getMaxExtent() == 0) ? 1E-3 : (0.001 * layerViewPanel().getWidth() / getMaxExtent()); if (viewBlackboard().get(USER_DEFINED_MIN_SCALE) != null) { return Math.min(minScale, viewBlackboard().getDouble(USER_DEFINED_MIN_SCALE)); } return minScale; } private double toScale(int sliderValue) throws NoninvertibleTransformException { return Math.pow( 10, ((slider.getMaximum() - sliderValue) * (MathUtil.base10Log(getMaxScale()) - MathUtil.base10Log(getMinScale())) / slider.getMaximum()) + MathUtil.base10Log(getMinScale())); } private void setComponentsEnabled(boolean componentsEnabled) { slider.setEnabled(componentsEnabled); label.setEnabled(componentsEnabled); } private static final String SCALE_KEY = ZoomBar.class.getName() + " - SCALE"; private static final String CENTRE_KEY = ZoomBar.class.getName() + " - CENTRE"; //Store centre-locked flag on blackboard rather than field because there could //be several zoom bars [Jon Aquino] private static final String CENTRE_LOCKED_KEY = ZoomBar.class.getName() + " - CENTRE LOCKED"; private static final String MIN_EXTENT_KEY = ZoomBar.class.getName() + " - MIN EXTENT"; private static final String USER_DEFINED_MIN_SCALE = ZoomBar.class.getName() + " - USER DEFINED MIN SCALE"; private static final String USER_DEFINED_MAX_SCALE = ZoomBar.class.getName() + " - USER DEFINED MAX SCALE"; private static final String MAX_EXTENT_KEY = ZoomBar.class.getName() + " - MAX EXTENT"; private Blackboard viewBlackboard() { return layerViewPanel() != null ? layerViewPanel().getBlackboard() : new Blackboard(); } private Blackboard modelBlackboard() { return layerViewPanel().getLayerManager().getBlackboard(); } private final LayerViewPanel dummyLayerViewPanel = new LayerViewPanel(new LayerManager(), new LayerViewPanelContext() { public void setStatusMessage(String message) { } public void warnUser(String warning) { } public void handleThrowable(Throwable t) { } }); private LayerViewPanel layerViewPanel() { if (!(frame.getActiveInternalFrame() instanceof LayerViewPanelProxy)) { return dummyLayerViewPanel; } return ((LayerViewPanelProxy) frame.getActiveInternalFrame()).getLayerViewPanel(); } void jbInit() throws Exception { this.setLayout(borderLayout1); label.setText(" "); slider.setPaintLabels(true); slider.setToolTipText(I18N.get("ui.zoom.ZoomBar.zoom")); slider.setMaximum(1000); this.add(slider, BorderLayout.CENTER); this.add(label, BorderLayout.EAST); } private void updateLabel() throws NoninvertibleTransformException { //Inexpensive. [Jon Aquino] label.setText(chooseGoodIncrement(getScale()).toString()); } private RoundQuantity chooseGoodIncrement(double scale) { return incrementChooser.chooseGoodIncrement( metricUnits, layerViewPanel().getWidth() / scale); } private Font sliderLabelFont = new Font("Dialog", Font.PLAIN, 10); private boolean showingSliderLabels; private void updateSliderLabels() throws NoninvertibleTransformException { //Expensive if the data cache has been cleared. [Jon Aquino] if (!showingSliderLabels) { return; } if (!(slider.getUI() instanceof BasicSliderUI)) { return; } Hashtable labelTable = new Hashtable(); final int LABEL_WIDTH = 60; int lastLabelPosition = -2 * LABEL_WIDTH; for (int i = 0; i < slider.getWidth(); i++) { if (i < (lastLabelPosition + LABEL_WIDTH)) { continue; } int sliderValue = ((BasicSliderUI) slider.getUI()).valueForXPosition(i); JLabel label = new JLabel(chooseGoodIncrement(toScale(sliderValue)).toString()); label.setFont(sliderLabelFont); labelTable.put(new Integer(sliderValue), label); lastLabelPosition = i; } if (labelTable.isEmpty()) { //Get here during initialization. [Jon Aquino] return; } slider.setLabelTable(labelTable); } /** * Return a Point2D in the view model from a model coordinate. */ // Added to implement Java2DConverter.PointConverter interface (used in getWireFrame) // [mmichaud 2007-05-27] public Point2D toViewPoint(Coordinate modelCoordinate) throws NoninvertibleTransformException { Point2D.Double pt = new Point2D.Double(modelCoordinate.x, modelCoordinate.y); return affineTransform.transform(pt, pt); } // Added to implement Java2DConverter.PointConverter modified on 2011-03-06 public Envelope getEnvelopeInModelCoordinates() { return lastGoodEnvelope; } }