001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
003: * for visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032: package com.vividsolutions.jump.workbench.ui.zoom;
033:
034: import java.awt.*;
035: import java.awt.event.*;
036: import java.awt.geom.AffineTransform;
037: import java.awt.geom.GeneralPath;
038: import java.awt.geom.NoninvertibleTransformException;
039: import java.awt.geom.Point2D;
040: import java.awt.geom.Rectangle2D;
041: import java.util.*;
042: import java.util.List;
043:
044: import javax.swing.*;
045: import javax.swing.Timer;
046: import javax.swing.plaf.basic.BasicSliderUI;
047: import javax.swing.event.InternalFrameAdapter;
048: import javax.swing.event.InternalFrameEvent;
049:
050: import com.vividsolutions.jts.geom.Coordinate;
051: import com.vividsolutions.jts.geom.Envelope;
052: import com.vividsolutions.jts.geom.Geometry;
053: import com.vividsolutions.jts.util.Assert;
054: import com.vividsolutions.jump.I18N;
055: import com.vividsolutions.jump.feature.Feature;
056: import com.vividsolutions.jump.feature.FeatureUtil;
057: import com.vividsolutions.jump.geom.EnvelopeUtil;
058: import com.vividsolutions.jump.geom.LineSegmentEnvelopeIntersector;
059: import com.vividsolutions.jump.util.Blackboard;
060: import com.vividsolutions.jump.util.CoordinateArrays;
061: import com.vividsolutions.jump.util.MathUtil;
062: import com.vividsolutions.jump.workbench.model.CategoryEvent;
063: import com.vividsolutions.jump.workbench.model.FeatureEvent;
064: import com.vividsolutions.jump.workbench.model.FeatureEventType;
065: import com.vividsolutions.jump.workbench.model.Layer;
066: import com.vividsolutions.jump.workbench.model.LayerEvent;
067: import com.vividsolutions.jump.workbench.model.LayerEventType;
068: import com.vividsolutions.jump.workbench.model.LayerListener;
069: import com.vividsolutions.jump.workbench.model.LayerManager;
070: import com.vividsolutions.jump.workbench.ui.GUIUtil;
071: import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
072: import com.vividsolutions.jump.workbench.ui.LayerViewPanelContext;
073: import com.vividsolutions.jump.workbench.ui.LayerViewPanelProxy;
074: import com.vividsolutions.jump.workbench.ui.Viewport;
075: import com.vividsolutions.jump.workbench.ui.ViewportListener;
076: import com.vividsolutions.jump.workbench.ui.WorkbenchFrame;
077: import com.vividsolutions.jump.workbench.ui.plugin.scalebar.IncrementChooser;
078: import com.vividsolutions.jump.workbench.ui.plugin.scalebar.MetricSystem;
079: import com.vividsolutions.jump.workbench.ui.plugin.scalebar.RoundQuantity;
080: import com.vividsolutions.jump.workbench.ui.plugin.scalebar.ScaleBarRenderer;
081: import com.vividsolutions.jump.workbench.ui.renderer.java2D.Java2DConverter;
082:
083: public class ZoomBar extends JPanel implements
084: Java2DConverter.PointConverter {
085: private int totalGeometries() {
086: int totalGeometries = 0;
087: // Restrict count to visible layers [mmichaud 2007-05-27]
088: for (Iterator i = layerViewPanel().getLayerManager()
089: .getVisibleLayers(true).iterator(); i.hasNext();) {
090: Layer layer = (Layer) i.next();
091: totalGeometries += layer.getFeatureCollectionWrapper()
092: .size();
093: }
094: return totalGeometries;
095: }
096:
097: private Envelope lastGoodEnvelope = null;
098: private WorkbenchFrame frame;
099: private BorderLayout borderLayout1 = new BorderLayout();
100: private JSlider slider = new JSlider();
101: private JLabel label = new JLabel();
102: private IncrementChooser incrementChooser = new IncrementChooser();
103: private Collection metricUnits = new MetricSystem(1).createUnits();
104:
105: // Add java2DConverter and affineTransform for coordinate decimation
106: private Java2DConverter java2DConverter;
107: private AffineTransform affineTransform;
108:
109: public ZoomBar(boolean showingSliderLabels,
110: boolean showingRightSideLabel, WorkbenchFrame frame)
111: throws NoninvertibleTransformException {
112: this .frame = frame;
113: this .showingSliderLabels = showingSliderLabels;
114: slider.addComponentListener(new ComponentAdapter() {
115: public void componentResized(ComponentEvent e) {
116: try {
117: updateComponents();
118: } catch (NoninvertibleTransformException x) {
119: //Eat it. [Jon Aquino]
120: }
121: }
122: });
123: if (showingSliderLabels) {
124: //Add a dummy label so that ZoomBars added to Toolboxes are
125: //packed properly. [Jon Aquino]
126: Hashtable labelTable = new Hashtable();
127: labelTable.put(new Integer(0), new JLabel(" "));
128: slider.setLabelTable(labelTable);
129: }
130: try {
131: jbInit();
132: } catch (Exception ex) {
133: ex.printStackTrace();
134: }
135: if (!showingRightSideLabel) {
136: remove(label);
137: }
138: label.addMouseListener(new MouseAdapter() {
139: public void mouseClicked(MouseEvent e) {
140: if (e.getClickCount() == 3
141: && SwingUtilities.isRightMouseButton(e)) {
142: viewBlackboard().put(USER_DEFINED_MIN_SCALE, null);
143: viewBlackboard().put(USER_DEFINED_MAX_SCALE, null);
144: clearModelCaches();
145: }
146: }
147: });
148: slider.addMouseMotionListener(new MouseMotionAdapter() {
149: //Use #mouseDragged rather than JSlider#stateChanged because we
150: //are interested in user-initiated slider changes, not programmatic
151: //slider changes. [Jon Aquino]
152: public void mouseDragged(MouseEvent e) {
153: try {
154: layerViewPanel()
155: .erase(
156: (Graphics2D) layerViewPanel()
157: .getGraphics());
158: drawWireframe();
159: ScaleBarRenderer scaleBarRenderer = (ScaleBarRenderer) layerViewPanel()
160: .getRenderingManager().getRenderer(
161: ScaleBarRenderer.CONTENT_ID);
162: if (scaleBarRenderer != null) {
163: scaleBarRenderer.paint(
164: (Graphics2D) layerViewPanel()
165: .getGraphics(), getScale());
166: }
167: updateLabel();
168: } catch (NoninvertibleTransformException x) {
169: //Eat it. [Jon Aquino]
170: }
171: }
172: });
173: if (slider.getUI() instanceof BasicSliderUI) {
174: slider.addMouseMotionListener(new MouseMotionAdapter() {
175: public void mouseMoved(MouseEvent e) {
176: if (layerViewPanel() == dummyLayerViewPanel) {
177: return;
178: }
179: try {
180: slider.setToolTipText(I18N
181: .get("ui.zoom.ZoomBar.zoom-to")
182: + " "
183: + chooseGoodIncrement(
184: toScale(((BasicSliderUI) slider
185: .getUI())
186: .valueForXPosition(e
187: .getX())))
188: .toString());
189: } catch (NoninvertibleTransformException x) {
190: slider.setToolTipText(I18N
191: .get("ui.zoom.ZoomBar.zoom"));
192: }
193: }
194: });
195: }
196: label.setPreferredSize(new Dimension(50, label.getHeight()));
197: slider.addKeyListener(new KeyAdapter() {
198: public void keyReleased(KeyEvent e) {
199: try {
200: if (e.getKeyCode() == KeyEvent.VK_LEFT
201: || e.getKeyCode() == KeyEvent.VK_RIGHT) {
202: gestureFinished();
203: }
204: } catch (NoninvertibleTransformException t) {
205: layerViewPanel().getContext().handleThrowable(t);
206: }
207: }
208: });
209: slider.addMouseListener(new MouseAdapter() {
210: public void mousePressed(MouseEvent e) {
211: if (!slider.isEnabled()) {
212: return;
213: }
214: layerViewPanel().getRenderingManager()
215: .setPaintingEnabled(false);
216: }
217:
218: public void mouseReleased(MouseEvent e) {
219: try {
220: gestureFinished();
221: } catch (NoninvertibleTransformException t) {
222: layerViewPanel().getContext().handleThrowable(t);
223: }
224: }
225: });
226:
227: //
228: // Whenever anything happens on an internal frame we want to do this.
229: //
230: GUIUtil.addInternalFrameListener(frame.getDesktopPane(),
231: GUIUtil.toInternalFrameListener(new ActionListener() {
232: public void actionPerformed(ActionEvent e) {
233: installListenersOnCurrentPanel();
234: try {
235: updateComponents();
236: } catch (NoninvertibleTransformException x) {
237: //Eat it. [Jon Aquino]
238: }
239: }
240: }));
241:
242: // added to use the decimator implemented in Java2DConverter [mmichaud 2007-05-27]
243: java2DConverter = new Java2DConverter(this , 2);
244:
245: installListenersOnCurrentPanel();
246: updateComponents();
247: }
248:
249: private void installListenersOnCurrentPanel() {
250: installViewListeners();
251: installModelListeners();
252: }
253:
254: private void installViewListeners() {
255:
256: //Use hash code to uniquely identify this zoom bar (there may be other
257: //zoom bars) [Jon Aquino]
258: String VIEW_LISTENERS_INSTALLED_KEY = Integer
259: .toHexString(hashCode())
260: + " - VIEW LISTENERS INSTALLED";
261: if (viewBlackboard().get(VIEW_LISTENERS_INSTALLED_KEY) != null) {
262: return;
263: }
264: if (layerViewPanel() == null) {
265: return;
266: }
267: layerViewPanel().getViewport().addListener(
268: new ViewportListener() {
269: public void zoomChanged(Envelope modelEnvelope) {
270: if (!viewBlackboard().get(CENTRE_LOCKED_KEY,
271: false)) {
272: viewBlackboard().put(CENTRE_KEY, null);
273: }
274: viewBlackboard().put(SCALE_KEY, null);
275: try {
276: if (layerViewPanel().getViewport()
277: .getScale() < getMinScale()) {
278: viewBlackboard().put(
279: USER_DEFINED_MIN_SCALE,
280: layerViewPanel().getViewport()
281: .getScale());
282: }
283: if (layerViewPanel().getViewport()
284: .getScale() > getMaxScale()) {
285: viewBlackboard().put(
286: USER_DEFINED_MAX_SCALE,
287: layerViewPanel().getViewport()
288: .getScale());
289: }
290: updateComponents();
291: } catch (NoninvertibleTransformException e) {
292: //Eat it. [Jon Aquino]
293: }
294: }
295: });
296: viewBlackboard()
297: .put(VIEW_LISTENERS_INSTALLED_KEY, new Object());
298: }
299:
300: private void installModelListeners() {
301:
302: //Use hash code to uniquely identify this zoom bar (there may be other
303: //zoom bars) [Jon Aquino]
304: String MODEL_LISTENERS_INSTALLED_KEY = Integer
305: .toHexString(hashCode())
306: + " - MODEL LISTENERS INSTALLED";
307: if (viewBlackboard().get(MODEL_LISTENERS_INSTALLED_KEY) != null) {
308: return;
309: }
310: if (layerViewPanel() == null) {
311: return;
312: }
313:
314: layerViewPanel().getLayerManager().addLayerListener(
315: new LayerListener() {
316: public void categoryChanged(CategoryEvent e) {
317: }
318:
319: public void featuresChanged(FeatureEvent e) {
320: if (e.getType() == FeatureEventType.ADDED
321: || e.getType() == FeatureEventType.DELETED
322: || e.getType() == FeatureEventType.GEOMETRY_MODIFIED) {
323: clearModelCaches();
324: }
325: }
326:
327: // add LayerEventType.VISIBILITY_CHANGED condition [mmichaud 2007-05-27]
328: public void layerChanged(LayerEvent e) {
329: if (e.getType() == LayerEventType.ADDED
330: || e.getType() == LayerEventType.REMOVED
331: || e.getType() == LayerEventType.VISIBILITY_CHANGED) {
332: clearModelCaches();
333: }
334: }
335: });
336: viewBlackboard().put(MODEL_LISTENERS_INSTALLED_KEY,
337: new Object());
338: }
339:
340: private void queueComponentUpdate() {
341: componentUpdateTimer.restart();
342: }
343:
344: /** Coalesces component updates */
345: private Timer componentUpdateTimer = GUIUtil
346: .createRestartableSingleEventTimer(200,
347: new ActionListener() {
348: public void actionPerformed(ActionEvent e) {
349: try {
350: updateComponents();
351: } catch (NoninvertibleTransformException x) {
352: //Eat it. [Jon Aquino]
353: }
354: }
355: });
356:
357: public void updateComponents()
358: throws NoninvertibleTransformException {
359: LayerViewPanel layerViewPanel = layerViewPanel();
360: if (layerViewPanel == dummyLayerViewPanel
361: || layerViewPanel == null) {
362: setComponentsEnabled(false);
363: return;
364: }
365: setComponentsEnabled(true);
366: //Must set slider value *before* updating the label on the right. [Jon Aquino]
367: //I'm currently hiding the label on the right, to save real estate. [Jon Aquino]
368: slider.setValue(toSliderValue(viewBlackboard().get(SCALE_KEY,
369: layerViewPanel.getViewport().getScale())));
370: updateLabel();
371: updateSliderLabels();
372: }
373:
374: private void gestureFinished()
375: throws NoninvertibleTransformException {
376: if (!slider.isEnabled()) {
377: return;
378: }
379: try {
380: viewBlackboard().put(CENTRE_LOCKED_KEY, true);
381: try {
382: layerViewPanel().getViewport().zoom(
383: proposedModelEnvelope());
384: } finally {
385: viewBlackboard().put(CENTRE_LOCKED_KEY, false);
386: }
387: } finally {
388: layerViewPanel().getRenderingManager().setPaintingEnabled(
389: true);
390: }
391: }
392:
393: private Envelope proposedModelEnvelope()
394: throws NoninvertibleTransformException {
395: Coordinate centre = (Coordinate) viewBlackboard().get(
396: CENTRE_KEY,
397: EnvelopeUtil.centre(layerViewPanel().getViewport()
398: .getEnvelopeInModelCoordinates()));
399: double width = layerViewPanel().getWidth() / getScale();
400: double height = layerViewPanel().getHeight() / getScale();
401: Envelope proposedModelEnvelope = new Envelope(centre.x
402: - (width / 2), centre.x + (width / 2), centre.y
403: - (height / 2), centre.y + (height / 2));
404: if (proposedModelEnvelope.getWidth() == 0
405: || proposedModelEnvelope.getHeight() == 0) {
406: //We're zoomed waaay out! Avoid infinite scale. [Jon Aquino]
407: proposedModelEnvelope = lastGoodEnvelope;
408: } else {
409: lastGoodEnvelope = proposedModelEnvelope;
410: }
411: return proposedModelEnvelope;
412: }
413:
414: /**
415: * Return the scale of the view according to the zoom bar
416: */
417: //getScale() public to implement Java2DConverter.PointConverter [mmichaud 2007-05-26]
418: public double getScale() throws NoninvertibleTransformException {
419: return toScale(slider.getValue());
420: }
421:
422: private Stroke stroke = new BasicStroke(1);
423:
424: private void drawWireframe() throws NoninvertibleTransformException {
425: Graphics2D g = (Graphics2D) layerViewPanel().getGraphics();
426: g.setColor(Color.lightGray);
427: g.setStroke(stroke);
428: g.draw(getWireFrame());
429: }
430:
431: private static final String SEGMENT_CACHE_KEY = ZoomBar.class
432: .getName()
433: + " - SEGMENT CACHE";
434:
435: private void clearModelCaches() {
436: //Use LayerManager blackboard for segment cache, so that multiple
437: //views can share it. [Jon Aquino]
438: modelBlackboard().put(SEGMENT_CACHE_KEY, null);
439: modelBlackboard().put(MIN_EXTENT_KEY, null);
440: modelBlackboard().put(MAX_EXTENT_KEY, null);
441: //It's expensive to recompute these cached values, so queue the call
442: //to #updateComponents [Jon Aquino]
443: queueComponentUpdate();
444: }
445:
446: private LineSegmentEnvelopeIntersector lineSegmentEnvelopeIntersector = new LineSegmentEnvelopeIntersector();
447:
448: // Modified by [mmichaud 2007-05-26] to use decimation algorithm from Java2DConverter
449: private Shape getWireFrame() throws NoninvertibleTransformException {
450: // affineTransform computed according to the zoombar slider (getScale)
451: affineTransform = Viewport.modelToViewTransform(getScale(),
452: new Point2D.Double(proposedModelEnvelope().getMinX(),
453: proposedModelEnvelope().getMinY()),
454: layerViewPanel().getSize().getHeight());
455: // view2D rectangle
456: Rectangle2D view2D = new Rectangle2D.Double(0.0, 0.0,
457: layerViewPanel().getWidth(), layerViewPanel()
458: .getWidth());
459: GeneralPath wireFrame = new GeneralPath();
460: ArrayList segments = new ArrayList(getSegmentCache());
461: //segments.addAll(toSegments(randomOnScreenGeometries()));
462: segments.addAll(toSegments(largeOnScreenGeometries()));
463: for (Iterator i = segments.iterator(); i.hasNext();) {
464: Coordinate[] coordinates = java2DConverter
465: .toViewCoordinates((Coordinate[]) i.next());
466: boolean drawing = false;
467:
468: for (int j = 1; j < coordinates.length; j++) {
469: if (!view2D.intersectsLine(coordinates[j - 1].x,
470: coordinates[j - 1].y, coordinates[j].x,
471: coordinates[j].y)) {
472: drawing = false;
473: continue;
474: }
475: if (!drawing) {
476: wireFrame.moveTo((float) coordinates[j - 1].x,
477: (float) coordinates[j - 1].y);
478: }
479: wireFrame.lineTo((float) coordinates[j].x,
480: (float) coordinates[j].y);
481: drawing = true;
482: }
483:
484: }
485: return wireFrame;
486:
487: }
488:
489: private Collection getSegmentCache()
490: throws NoninvertibleTransformException {
491: //Use LayerManager blackboard for segment cache, so that multiple
492: //views can share it. [Jon Aquino]
493: if (modelBlackboard().get(SEGMENT_CACHE_KEY) == null) {
494: //modelBlackboard().put(SEGMENT_CACHE_KEY, toSegments(randomGeometries()));
495: modelBlackboard().put(SEGMENT_CACHE_KEY,
496: toSegments(largeGeometries()));
497: //
498: // We only want to do this when a frame closes. If we don't clear the
499: // cache in the blackboard then we'll get a memory leak.
500: //
501: frame.getActiveInternalFrame().addInternalFrameListener(
502: new InternalFrameAdapter() {
503: public void internalFrameClosing(
504: InternalFrameEvent e) {
505: modelBlackboard().put(SEGMENT_CACHE_KEY,
506: null);
507: }
508: });
509: }
510: return (Collection) modelBlackboard().get(SEGMENT_CACHE_KEY);
511: }
512:
513: private Collection toSegments(Collection geometries) {
514: ArrayList segments = new ArrayList();
515: for (Iterator i = geometries.iterator(); i.hasNext();) {
516: Geometry geometry = (Geometry) i.next();
517: segments.addAll(CoordinateArrays.toCoordinateArrays(
518: geometry, false));
519: }
520: return segments;
521: }
522:
523: // Replace RANDOM_ONSCREEN_GEOMETRIES by LARGE_ONSCREEN_GEOMETRIES
524: // private static final int RANDOM_ONSCREEN_GEOMETRIES = 100;
525: // private static final int RANDOM_GEOMETRIES = 100;
526: private static final int LARGE_GEOMETRIES = 100;
527: private static final int LARGE_ONSCREEN_GEOMETRIES = 200;
528:
529: // start [mmichaud]
530: // additional code to replace randomGeometries by a largestGeometries approach
531: // one bad side effect of random geometries approach was visible for a big set of points
532: // and a small set of polygons : polygons were not visible because of the proportional
533: // selection of geometries, and points are not displayed :-(
534:
535: static final Comparator MAX_SIZE_COMPARATOR = new Comparator() {
536: public int compare(Object f1, Object f2) {
537: Envelope env1 = ((Feature) f1).getGeometry()
538: .getEnvelopeInternal();
539: Envelope env2 = ((Feature) f2).getGeometry()
540: .getEnvelopeInternal();
541: double size1 = Math.max(env1.getWidth(), env1.getHeight());
542: double size2 = Math.max(env2.getWidth(), env2.getHeight());
543: return size1 < size2 ? 1 : (size1 > size2 ? -1 : 0);
544: }
545: };
546:
547: private Collection largeGeometries(int maxSize, List features) {
548: Collections.sort(features, MAX_SIZE_COMPARATOR);
549: List geometries = new ArrayList();
550: for (int i = 0, max = Math.min(maxSize, features.size()); i < max; i++) {
551: geometries.add(((Feature) features.get(i)).getGeometry());
552: }
553: //System.out.println("" + geometries.size() + "/" + features.size());
554: return geometries;
555: }
556:
557: private Collection largeOnScreenGeometries() {
558: List onScreenFeatures = new ArrayList();
559: if (totalGeometries() == 0) {
560: return onScreenFeatures;
561: }
562: // Use proposedModelEnvelope (dynamically computed while the mouse is dragged)
563: // instead of layerViewPanel().getViewport().getEnvelopeInModelCoordinates()
564: // [mmichaud 2007-05-27]
565: Envelope modelEnvelope;
566: try {
567: modelEnvelope = proposedModelEnvelope();
568: } catch (NoninvertibleTransformException e) {
569: modelEnvelope = layerViewPanel().getViewport()
570: .getEnvelopeInModelCoordinates();
571: }
572: // Restrict to visible layers [mmichaud 2007-05-27]
573: for (Iterator it = layerViewPanel().getLayerManager()
574: .getVisibleLayers(true).iterator(); it.hasNext();) {
575: Layer layer = (Layer) it.next();
576: // Select features intersecting the window
577: List visibleFeatures = layer.getFeatureCollectionWrapper()
578: .query(modelEnvelope);
579: if (visibleFeatures.size() < 1000) {
580: onScreenFeatures.addAll(layer
581: .getFeatureCollectionWrapper().query(
582: modelEnvelope));
583: }
584: // If there are more than 1000 visible features in this layer
585: // select a maximum of 2000 features stepping through the list
586: else {
587: int step = visibleFeatures.size() / 1000;
588: for (int i = 0, max = visibleFeatures.size(); i < max; i += step) {
589: onScreenFeatures.add(visibleFeatures.get(i));
590: }
591: }
592: }
593: return largeGeometries(LARGE_ONSCREEN_GEOMETRIES,
594: onScreenFeatures);
595: }
596:
597: private Collection largeGeometries() {
598: ArrayList largeGeometries = new ArrayList();
599: if (totalGeometries() == 0) {
600: return largeGeometries;
601: }
602: Envelope modelEnvelope = layerViewPanel().getViewport()
603: .getEnvelopeInModelCoordinates();
604: for (Iterator i = layerViewPanel().getLayerManager()
605: .getVisibleLayers(true).iterator(); i.hasNext();) {
606: Layer layer = (Layer) i.next();
607: List visibleFeatures = layer.getFeatureCollectionWrapper()
608: .query(modelEnvelope);
609: largeGeometries.addAll(layer.getFeatureCollectionWrapper()
610: .getFeatures());
611: }
612: return largeGeometries(LARGE_GEOMETRIES, largeGeometries);
613: }
614:
615: // end [mmichaud]
616:
617: // Replaced Random geometries by large geometries
618: /*
619: private Collection randomGeometries(int maxSize, List features) {
620: if (features.size() <= maxSize) {
621: return FeatureUtil.toGeometries(features);
622: }
623: ArrayList randomGeometries = new ArrayList();
624: for (int j = 0; j < maxSize; j++) {
625: randomGeometries.add(
626: ((Feature) features.get((int) (Math.random() * features.size()))).getGeometry());
627: }
628: return randomGeometries;
629: }
630:
631: private Collection randomOnScreenGeometries() {
632: ArrayList randomOnScreenGeometries = new ArrayList();
633: // Avoid method computation inside the loop [mmichaud]
634: int totalGeometries = totalGeometries();
635: if (totalGeometries == 0) {
636: return randomOnScreenGeometries;
637: }
638: // Use proposedModelEnvelope (dynamically computed while the mouse is dragged)
639: // instead of layerViewPanel().getViewport().getEnvelopeInModelCoordinates()
640: // [mmichaud 2007-05-27]
641: Envelope modelEnvelope;
642: try {
643: modelEnvelope = proposedModelEnvelope();
644: }
645: catch(NoninvertibleTransformException e) {
646: modelEnvelope = layerViewPanel().getViewport().getEnvelopeInModelCoordinates();
647: }
648: // Restrict to visible layers [mmichaud 2007-05-27]
649: for (Iterator i = layerViewPanel().getLayerManager().getVisibleLayers(true).iterator(); i.hasNext();) {
650: Layer layer = (Layer) i.next();
651: randomOnScreenGeometries.addAll(
652: randomGeometries(
653: RANDOM_ONSCREEN_GEOMETRIES
654: * layer.getFeatureCollectionWrapper().size()
655: / totalGeometries(),
656: layer.getFeatureCollectionWrapper().query(
657: //layerViewPanel().getViewport().getEnvelopeInModelCoordinates())));
658: modelEnvelope)));
659: }
660: return randomOnScreenGeometries;
661: }
662:
663: private Collection randomGeometries() {
664: ArrayList randomGeometries = new ArrayList();
665: if (totalGeometries() == 0) {
666: return randomGeometries;
667: }
668: // Restrict to visible layers [mmichaud 2007-05-27]
669: for (Iterator i = layerViewPanel().getLayerManager().getVisibleLayers(true).iterator(); i.hasNext();) {
670: Layer layer = (Layer) i.next();
671: randomGeometries.addAll(
672: randomGeometries(
673: RANDOM_GEOMETRIES
674: * layer.getFeatureCollectionWrapper().size()
675: / totalGeometries(),
676: layer.getFeatureCollectionWrapper().getFeatures()));
677:
678: }
679: return randomGeometries;
680: }
681: */
682:
683: private int toSliderValue(double scale)
684: throws NoninvertibleTransformException {
685: return slider.getMaximum()
686: - (int) (slider.getMaximum()
687: * (MathUtil.base10Log(scale) - MathUtil
688: .base10Log(getMinScale())) / (MathUtil
689: .base10Log(getMaxScale()) - MathUtil
690: .base10Log(getMinScale())));
691: }
692:
693: private double getMinExtent()
694: throws NoninvertibleTransformException {
695: if (modelBlackboard().get(MIN_EXTENT_KEY) == null) {
696: double smallSegmentLength = chooseSmallSegmentLength(getSegmentCache());
697: //-1 smallSegmentLength means there is no data or the data are all
698: //points (i.e. no segments). [Jon Aquino]
699: if (smallSegmentLength == -1) {
700: return -1;
701: }
702: modelBlackboard().put(MIN_EXTENT_KEY, smallSegmentLength);
703: }
704: Assert.isTrue(modelBlackboard().getDouble(MIN_EXTENT_KEY) > 0);
705: return modelBlackboard().getDouble(MIN_EXTENT_KEY);
706: }
707:
708: private double chooseSmallSegmentLength(Collection segmentCache) {
709: int segmentsChecked = 0;
710: double smallSegmentLength = -1;
711: for (Iterator i = segmentCache.iterator(); i.hasNext();) {
712: Coordinate[] coordinates = (Coordinate[]) i.next();
713: for (int j = 1; j < coordinates.length; j++) {
714: double segmentLength = coordinates[j]
715: .distance(coordinates[j - 1]);
716: segmentsChecked++;
717: if (segmentLength > 0
718: && (smallSegmentLength == -1 || segmentLength < smallSegmentLength)) {
719: smallSegmentLength = segmentLength;
720: }
721: if (segmentsChecked > 100) {
722: break;
723: }
724: }
725: if (segmentsChecked > 100) {
726: break;
727: }
728: }
729: return smallSegmentLength;
730: }
731:
732: private double getMaxExtent()
733: throws NoninvertibleTransformException {
734: if (modelBlackboard().get(MAX_EXTENT_KEY) == null) {
735: if (getSegmentCache().isEmpty()) {
736: return -1;
737: }
738: modelBlackboard().put(
739: MAX_EXTENT_KEY,
740: layerViewPanel().getLayerManager()
741: .getEnvelopeOfAllLayers().getWidth());
742: }
743: return modelBlackboard().getDouble(MAX_EXTENT_KEY);
744: }
745:
746: private double getMaxScale() throws NoninvertibleTransformException {
747: double maxScale = (getMinExtent() == -1 || getMinExtent() == 0) ? 1E3
748: : (1000 * layerViewPanel().getWidth() / getMinExtent());
749: if (viewBlackboard().get(USER_DEFINED_MAX_SCALE) != null) {
750: return Math.max(maxScale, viewBlackboard().getDouble(
751: USER_DEFINED_MAX_SCALE));
752: }
753: return maxScale;
754: }
755:
756: private double getMinScale() throws NoninvertibleTransformException {
757: double minScale = (getMaxExtent() == -1 || getMaxExtent() == 0) ? 1E-3
758: : (0.001 * layerViewPanel().getWidth() / getMaxExtent());
759: if (viewBlackboard().get(USER_DEFINED_MIN_SCALE) != null) {
760: return Math.min(minScale, viewBlackboard().getDouble(
761: USER_DEFINED_MIN_SCALE));
762: }
763: return minScale;
764: }
765:
766: private double toScale(int sliderValue)
767: throws NoninvertibleTransformException {
768: return Math.pow(10, ((slider.getMaximum() - sliderValue)
769: * (MathUtil.base10Log(getMaxScale()) - MathUtil
770: .base10Log(getMinScale())) / slider
771: .getMaximum())
772: + MathUtil.base10Log(getMinScale()));
773: }
774:
775: private void setComponentsEnabled(boolean componentsEnabled) {
776: slider.setEnabled(componentsEnabled);
777: label.setEnabled(componentsEnabled);
778: }
779:
780: private static final String SCALE_KEY = ZoomBar.class.getName()
781: + " - SCALE";
782: private static final String CENTRE_KEY = ZoomBar.class.getName()
783: + " - CENTRE";
784: //Store centre-locked flag on blackboard rather than field because there could
785: //be several zoom bars [Jon Aquino]
786: private static final String CENTRE_LOCKED_KEY = ZoomBar.class
787: .getName()
788: + " - CENTRE LOCKED";
789: private static final String MIN_EXTENT_KEY = ZoomBar.class
790: .getName()
791: + " - MIN EXTENT";
792: private static final String USER_DEFINED_MIN_SCALE = ZoomBar.class
793: .getName()
794: + " - USER DEFINED MIN SCALE";
795: private static final String USER_DEFINED_MAX_SCALE = ZoomBar.class
796: .getName()
797: + " - USER DEFINED MAX SCALE";
798: private static final String MAX_EXTENT_KEY = ZoomBar.class
799: .getName()
800: + " - MAX EXTENT";
801:
802: private Blackboard viewBlackboard() {
803: return layerViewPanel() != null ? layerViewPanel()
804: .getBlackboard() : new Blackboard();
805: }
806:
807: private Blackboard modelBlackboard() {
808: return layerViewPanel().getLayerManager().getBlackboard();
809: }
810:
811: private final LayerViewPanel dummyLayerViewPanel = new LayerViewPanel(
812: new LayerManager(), new LayerViewPanelContext() {
813:
814: public void setStatusMessage(String message) {
815: }
816:
817: public void warnUser(String warning) {
818: }
819:
820: public void handleThrowable(Throwable t) {
821: }
822:
823: });
824:
825: private LayerViewPanel layerViewPanel() {
826: if (!(frame.getActiveInternalFrame() instanceof LayerViewPanelProxy)) {
827: return dummyLayerViewPanel;
828: }
829: return ((LayerViewPanelProxy) frame.getActiveInternalFrame())
830: .getLayerViewPanel();
831: }
832:
833: void jbInit() throws Exception {
834: this .setLayout(borderLayout1);
835: label.setText(" ");
836: slider.setPaintLabels(true);
837: slider.setToolTipText(I18N.get("ui.zoom.ZoomBar.zoom"));
838: slider.setMaximum(1000);
839: this .add(slider, BorderLayout.CENTER);
840: this .add(label, BorderLayout.EAST);
841: }
842:
843: private void updateLabel() throws NoninvertibleTransformException {
844: //Inexpensive. [Jon Aquino]
845: label.setText(chooseGoodIncrement(getScale()).toString());
846: }
847:
848: private RoundQuantity chooseGoodIncrement(double scale) {
849: return incrementChooser.chooseGoodIncrement(metricUnits,
850: layerViewPanel().getWidth() / scale);
851: }
852:
853: private Font sliderLabelFont = new Font("Dialog", Font.PLAIN, 10);
854: private boolean showingSliderLabels;
855:
856: private void updateSliderLabels()
857: throws NoninvertibleTransformException {
858: //Expensive if the data cache has been cleared. [Jon Aquino]
859: if (!showingSliderLabels) {
860: return;
861: }
862: if (!(slider.getUI() instanceof BasicSliderUI)) {
863: return;
864: }
865: Hashtable labelTable = new Hashtable();
866: final int LABEL_WIDTH = 60;
867: int lastLabelPosition = -2 * LABEL_WIDTH;
868: for (int i = 0; i < slider.getWidth(); i++) {
869: if (i < (lastLabelPosition + LABEL_WIDTH)) {
870: continue;
871: }
872: int sliderValue = ((BasicSliderUI) slider.getUI())
873: .valueForXPosition(i);
874: JLabel label = new JLabel(chooseGoodIncrement(
875: toScale(sliderValue)).toString());
876: label.setFont(sliderLabelFont);
877: labelTable.put(new Integer(sliderValue), label);
878: lastLabelPosition = i;
879: }
880: if (labelTable.isEmpty()) {
881: //Get here during initialization. [Jon Aquino]
882: return;
883: }
884: slider.setLabelTable(labelTable);
885: }
886:
887: /**
888: * Return a Point2D in the view model from a model coordinate.
889: */
890: // Added to implement Java2DConverter.PointConverter interface (used in getWireFrame)
891: // [mmichaud 2007-05-27]
892: public Point2D toViewPoint(Coordinate modelCoordinate)
893: throws NoninvertibleTransformException {
894: Point2D.Double pt = new Point2D.Double(modelCoordinate.x,
895: modelCoordinate.y);
896: return affineTransform.transform(pt, pt);
897: }
898:
899: }
|