Introduce Swing based map viewer
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 5 May 2011 14:35:20 +0000 (14:35 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 5 May 2011 14:35:20 +0000 (14:35 +0000)
NEW - bug 31: Add Swing based map viewer in RCP
https://bugzilla.argeo.org/show_bug.cgi?id=31

git-svn-id: https://svn.argeo.org/commons/trunk@4508 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

gis/plugins/org.argeo.gis.ui.rcp.swing/.classpath [new file with mode: 0644]
gis/plugins/org.argeo.gis.ui.rcp.swing/.project [new file with mode: 0644]
gis/plugins/org.argeo.gis.ui.rcp.swing/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
gis/plugins/org.argeo.gis.ui.rcp.swing/META-INF/MANIFEST.MF [new file with mode: 0644]
gis/plugins/org.argeo.gis.ui.rcp.swing/META-INF/spring/mapviewer.xml [new file with mode: 0644]
gis/plugins/org.argeo.gis.ui.rcp.swing/META-INF/spring/osgi.xml [new file with mode: 0644]
gis/plugins/org.argeo.gis.ui.rcp.swing/build.properties [new file with mode: 0644]
gis/plugins/org.argeo.gis.ui.rcp.swing/src/main/java/org/argeo/gis/ui/rcp/swing/SwingMapControlCreator.java [new file with mode: 0644]
gis/plugins/org.argeo.gis.ui.rcp.swing/src/main/java/org/argeo/gis/ui/rcp/swing/SwingMapViewer.java [new file with mode: 0644]
gis/plugins/org.argeo.gis.ui.rcp.swing/src/main/java/org/argeo/gis/ui/rcp/swing/VersatileZoomTool.java [new file with mode: 0644]

diff --git a/gis/plugins/org.argeo.gis.ui.rcp.swing/.classpath b/gis/plugins/org.argeo.gis.ui.rcp.swing/.classpath
new file mode 100644 (file)
index 0000000..92f19d2
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/gis/plugins/org.argeo.gis.ui.rcp.swing/.project b/gis/plugins/org.argeo.gis.ui.rcp.swing/.project
new file mode 100644 (file)
index 0000000..83b1790
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.argeo.gis.ui.rcp.swing</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/gis/plugins/org.argeo.gis.ui.rcp.swing/.settings/org.eclipse.jdt.core.prefs b/gis/plugins/org.argeo.gis.ui.rcp.swing/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..ca891a6
--- /dev/null
@@ -0,0 +1,8 @@
+#Thu May 05 15:41:33 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/gis/plugins/org.argeo.gis.ui.rcp.swing/META-INF/MANIFEST.MF b/gis/plugins/org.argeo.gis.ui.rcp.swing/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..4a2380d
--- /dev/null
@@ -0,0 +1,25 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: RCP specific components based on Swing
+Bundle-SymbolicName: org.argeo.gis.ui.rcp.swing
+Bundle-Version: 0.3.2.SNAPSHOT
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Import-Package: com.vividsolutions.jts.geom;version="1.10.0",
+ javax.jcr;version="2.0.0",
+ org.argeo.geotools.jcr,
+ org.argeo.gis.ui,
+ org.eclipse.swt,
+ org.eclipse.swt.awt,
+ org.eclipse.swt.widgets,
+ org.geotools.data,
+ org.geotools.geometry,
+ org.geotools.geometry.jts,
+ org.geotools.map,
+ org.geotools.renderer,
+ org.geotools.renderer.lite,
+ org.geotools.swing,
+ org.geotools.swing.event,
+ org.geotools.swing.tool,
+ org.opengis.feature,
+ org.opengis.feature.simple,
+ org.opengis.geometry
diff --git a/gis/plugins/org.argeo.gis.ui.rcp.swing/META-INF/spring/mapviewer.xml b/gis/plugins/org.argeo.gis.ui.rcp.swing/META-INF/spring/mapviewer.xml
new file mode 100644 (file)
index 0000000..b163efd
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
+       xsi:schemaLocation="
+               http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+       <bean id="mapControlCreator" class="org.argeo.gis.ui.rcp.swing.SwingMapControlCreator">
+               <property name="geoJcrMapper" ref="geoJcrMapper" />
+       </bean>
+
+</beans>
diff --git a/gis/plugins/org.argeo.gis.ui.rcp.swing/META-INF/spring/osgi.xml b/gis/plugins/org.argeo.gis.ui.rcp.swing/META-INF/spring/osgi.xml
new file mode 100644 (file)
index 0000000..7a4ec53
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
+       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
+       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
+       http://www.springframework.org/schema/beans   \r
+       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
+\r
+       <reference id="geoJcrMapper" interface="org.argeo.geotools.jcr.GeoJcrMapper" />\r
+\r
+       <service ref="mapControlCreator" interface="org.argeo.gis.ui.MapControlCreator" />\r
+\r
+</beans:beans>
\ No newline at end of file
diff --git a/gis/plugins/org.argeo.gis.ui.rcp.swing/build.properties b/gis/plugins/org.argeo.gis.ui.rcp.swing/build.properties
new file mode 100644 (file)
index 0000000..5fc538b
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/main/java/
+output.. = target/classes/
+bin.includes = META-INF/,\
+               .
diff --git a/gis/plugins/org.argeo.gis.ui.rcp.swing/src/main/java/org/argeo/gis/ui/rcp/swing/SwingMapControlCreator.java b/gis/plugins/org.argeo.gis.ui.rcp.swing/src/main/java/org/argeo/gis/ui/rcp/swing/SwingMapControlCreator.java
new file mode 100644 (file)
index 0000000..80c56f8
--- /dev/null
@@ -0,0 +1,22 @@
+package org.argeo.gis.ui.rcp.swing;
+
+import javax.jcr.Node;
+
+import org.argeo.geotools.jcr.GeoJcrMapper;
+import org.argeo.gis.ui.MapControlCreator;
+import org.argeo.gis.ui.MapViewer;
+import org.eclipse.swt.widgets.Composite;
+
+/** Creates a Swing map viewer */
+public class SwingMapControlCreator implements MapControlCreator {
+       private GeoJcrMapper geoJcrMapper;
+
+       public MapViewer createMapControl(Node context, Composite parent) {
+               return new SwingMapViewer(context, geoJcrMapper, parent);
+       }
+
+       public void setGeoJcrMapper(GeoJcrMapper geoJcrMapper) {
+               this.geoJcrMapper = geoJcrMapper;
+       }
+
+}
diff --git a/gis/plugins/org.argeo.gis.ui.rcp.swing/src/main/java/org/argeo/gis/ui/rcp/swing/SwingMapViewer.java b/gis/plugins/org.argeo.gis.ui.rcp.swing/src/main/java/org/argeo/gis/ui/rcp/swing/SwingMapViewer.java
new file mode 100644 (file)
index 0000000..6a1ed0f
--- /dev/null
@@ -0,0 +1,47 @@
+package org.argeo.gis.ui.rcp.swing;
+
+import java.awt.Frame;
+
+import javax.jcr.Node;
+
+import org.argeo.geotools.jcr.GeoJcrMapper;
+import org.argeo.gis.ui.AbstractMapViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.awt.SWT_AWT;
+import org.eclipse.swt.widgets.Composite;
+import org.geotools.data.FeatureSource;
+import org.geotools.map.DefaultMapContext;
+import org.geotools.renderer.lite.StreamingRenderer;
+import org.geotools.swing.JMapPane;
+import org.opengis.feature.simple.SimpleFeature;
+import org.opengis.feature.simple.SimpleFeatureType;
+
+public class SwingMapViewer extends AbstractMapViewer {
+       private Composite embedded;
+       private JMapPane mapPane;
+       private VersatileZoomTool versatileZoomTool;
+
+       public SwingMapViewer(Node context, GeoJcrMapper geoJcrMapper,
+                       Composite parent) {
+               super(context, geoJcrMapper);
+
+               embedded = new Composite(parent, SWT.EMBEDDED | SWT.NO_BACKGROUND);
+               Frame frame = SWT_AWT.new_Frame(embedded);
+
+               mapPane = new JMapPane(new StreamingRenderer(), new DefaultMapContext());
+               versatileZoomTool = new VersatileZoomTool();
+               mapPane.setCursorTool(versatileZoomTool);
+
+               frame.add(mapPane);
+
+               setControl(embedded);
+       }
+
+       @Override
+       protected void addFeatureSource(String path,
+                       FeatureSource<SimpleFeatureType, SimpleFeature> featureSource) {
+               // TODO: deal with style and rasters
+               mapPane.getMapContext().addLayer(featureSource, null);
+       }
+
+}
diff --git a/gis/plugins/org.argeo.gis.ui.rcp.swing/src/main/java/org/argeo/gis/ui/rcp/swing/VersatileZoomTool.java b/gis/plugins/org.argeo.gis.ui.rcp.swing/src/main/java/org/argeo/gis/ui/rcp/swing/VersatileZoomTool.java
new file mode 100644 (file)
index 0000000..6b7b80c
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *         http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.argeo.gis.ui.rcp.swing;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.awt.geom.Point2D;
+
+import javax.swing.SwingUtilities;
+import javax.swing.event.MouseInputAdapter;
+
+import org.geotools.geometry.DirectPosition2D;
+import org.geotools.geometry.Envelope2D;
+import org.geotools.geometry.jts.ReferencedEnvelope;
+import org.geotools.swing.JMapPane;
+import org.geotools.swing.event.MapMouseEvent;
+import org.geotools.swing.tool.AbstractZoomTool;
+
+public class VersatileZoomTool extends AbstractZoomTool {
+       // private Log log = LogFactory.getLog(VersatileZoomTool.class);
+
+       // private static final ResourceBundle stringRes = ResourceBundle
+       // .getBundle("org/geotools/swing/Text");
+
+       // Cursors
+       private Cursor zoomInCursor;
+       private Cursor panCursor;
+       private Cursor defaultCursor;
+
+       // Variable values
+       private Point2D startDragPos;
+       private Point panePos;
+       private boolean computingZoomBox;
+       private boolean panning;
+
+       private Point2D fieldPosition;
+
+       /**
+        * Constructor
+        */
+       public VersatileZoomTool() {
+               // Toolkit tk = Toolkit.getDefaultToolkit();
+               // zoomInCursor = tk.createCustomCursor(new ImageIcon(getClass()
+               // .getResource("/org/geotools/swing/icons/mActionZoomIn.png"))
+               // .getImage(), new Point(14, 9), stringRes
+               // .getString("tool_name_zoom_in"));
+               zoomInCursor = new Cursor(Cursor.SE_RESIZE_CURSOR);
+               // panCursor = tk.createCustomCursor(new
+               // ImageIcon(getClass().getResource(
+               // "/org/geotools/swing/icons/mActionPan.png")).getImage(),
+               // new Point(15, 15), stringRes.getString("tool_name_pan"));
+               panCursor = new Cursor(Cursor.HAND_CURSOR);
+               defaultCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
+
+               startDragPos = new DirectPosition2D();
+               computingZoomBox = false;
+               panning = false;
+
+       }
+
+       /**
+        * Zoom in by the currently set increment, with the map centred at the
+        * location (in world coords) of the mouse click
+        * 
+        * @param e
+        *            map mapPane mouse event
+        */
+       @Override
+       public void onMouseClicked(MapMouseEvent e) {
+               if (SwingUtilities.isLeftMouseButton(e))
+                       centerMapToEvent(e, getZoom());
+               else if (SwingUtilities.isRightMouseButton(e))
+                       centerMapToEvent(e, 1 / getZoom());
+               else if (SwingUtilities.isMiddleMouseButton(e)) {
+                       if (fieldPosition != null) {
+                               Envelope2D env = new Envelope2D();
+                               final double increment = 0.1d;
+                               env.setFrameFromDiagonal(fieldPosition.getX() - increment,
+                                               fieldPosition.getY() - increment, fieldPosition.getX()
+                                                               + increment, fieldPosition.getY() + increment);
+                               getMapPane().setDisplayArea(env);
+                       }
+               }
+       }
+
+       protected void centerMapToEvent(MapMouseEvent e, Double zoomArg) {
+               Rectangle paneArea = getMapPane().getVisibleRect();
+               DirectPosition2D mapPos = e.getMapPosition();
+
+               double scale = getMapPane().getWorldToScreenTransform().getScaleX();
+               double newScale = scale * zoomArg;
+
+               DirectPosition2D corner = new DirectPosition2D(mapPos.getX() - 0.5d
+                               * paneArea.getWidth() / newScale, mapPos.getY() + 0.5d
+                               * paneArea.getHeight() / newScale);
+
+               Envelope2D newMapArea = new Envelope2D();
+               newMapArea.setFrameFromCenter(mapPos, corner);
+               getMapPane().setDisplayArea(newMapArea);
+       }
+
+       /**
+        * Records the map position of the mouse event in case this button press is
+        * the beginning of a mouse drag
+        * 
+        * @param ev
+        *            the mouse event
+        */
+       @Override
+       public void onMousePressed(MapMouseEvent ev) {
+               if (SwingUtilities.isLeftMouseButton(ev)) {
+                       startDragPos = new DirectPosition2D();
+                       startDragPos.setLocation(ev.getMapPosition());
+               } else if (SwingUtilities.isMiddleMouseButton(ev)
+                               || SwingUtilities.isRightMouseButton(ev)) {
+                       panePos = ev.getPoint();
+                       panning = true;
+               }
+       }
+
+       /**
+        * Records that the mouse is being dragged
+        * 
+        * @param ev
+        *            the mouse event
+        */
+       @Override
+       public void onMouseDragged(MapMouseEvent ev) {
+               if (SwingUtilities.isLeftMouseButton(ev)) {
+                       computingZoomBox = true;
+               } else if (panning) {
+                       Point pos = ev.getPoint();
+                       if (!pos.equals(panePos)) {
+                               getMapPane().moveImage(pos.x - panePos.x, pos.y - panePos.y);
+                               panePos = pos;
+                       }
+               }
+               getMapPane().setCursor(getCursor());
+       }
+
+       /**
+        * If the mouse was dragged, determines the bounds of the box that the user
+        * defined and passes this to the mapPane's
+        * {@link org.geotools.swing.JMapPane#setDisplayArea(org.opengis.geometry.Envelope) }
+        * method
+        * 
+        * @param ev
+        *            the mouse event
+        */
+       @Override
+       public void onMouseReleased(MapMouseEvent ev) {
+               if (computingZoomBox && !ev.getPoint().equals(startDragPos)) {
+                       Envelope2D env = new Envelope2D();
+                       env.setFrameFromDiagonal(startDragPos, ev.getMapPosition());
+                       computingZoomBox = false;
+                       getMapPane().setDisplayArea(env);
+               } else if (panning) {
+                       panning = false;
+                       getMapPane().repaint();
+               }
+               getMapPane().setCursor(getCursor());
+       }
+
+       /**
+        * Get the mouse cursor for this tool
+        */
+       @Override
+       public Cursor getCursor() {
+               if (computingZoomBox)
+                       return zoomInCursor;
+               else if (panning)
+                       return panCursor;
+               else
+                       return defaultCursor;
+       }
+
+       /**
+        * We use a custom drag box
+        */
+       @Override
+       public boolean drawDragBox() {
+               return false;
+       }
+
+       @Override
+       public void setMapPane(JMapPane pane) {
+               super.setMapPane(pane);
+               VariableDragBox dragBox = new VariableDragBox();
+               getMapPane().addMouseListener(dragBox);
+               getMapPane().addMouseMotionListener(dragBox);
+               getMapPane().addMouseWheelListener(new MouseWheelListener() {
+                       private double clickToZoom = 0.1; // 1 wheel click is 10% zoom
+
+                       public void mouseWheelMoved(MouseWheelEvent ev) {
+                               int clicks = ev.getWheelRotation();
+                               // -ve means wheel moved up, +ve means down
+                               int sign = (clicks < 0 ? -1 : 1);
+
+                               ReferencedEnvelope env = getMapPane().getDisplayArea();
+                               if (env == null)
+                                       return;
+                               double width = env.getWidth();
+                               double delta = width * clickToZoom * sign;
+
+                               env.expandBy(delta);
+                               getMapPane().setDisplayArea(env);
+                               getMapPane().repaint();
+                       }
+               });
+       }
+
+       public void setFieldPosition(Point2D fieldPosition) {
+               this.fieldPosition = fieldPosition;
+       }
+
+       /**
+        * Custom drag box (hacked from JMapPane) so that we can change the behavior
+        * depending on whether we pan or zoom.
+        */
+       private class VariableDragBox extends MouseInputAdapter {
+
+               private Point startPos;
+               private Rectangle rect;
+               private boolean dragged;
+
+               VariableDragBox() {
+                       rect = new Rectangle();
+                       dragged = false;
+               }
+
+               @Override
+               public void mousePressed(MouseEvent e) {
+                       startPos = new Point(e.getPoint());
+               }
+
+               @Override
+               public void mouseDragged(MouseEvent e) {
+                       if (computingZoomBox) {
+                               Graphics2D g2D = (Graphics2D) getMapPane().getGraphics();
+                               g2D.setColor(Color.WHITE);
+                               g2D.setXORMode(Color.RED);
+                               if (dragged) {
+                                       g2D.drawRect(rect.x, rect.y, rect.width, rect.height);
+                               }
+
+                               rect.setFrameFromDiagonal(startPos, e.getPoint());
+                               g2D.drawRect(rect.x, rect.y, rect.width, rect.height);
+
+                               dragged = true;
+                       }
+               }
+
+               @Override
+               public void mouseReleased(MouseEvent e) {
+                       if (dragged) {
+                               Graphics2D g2D = (Graphics2D) getMapPane().getGraphics();
+                               g2D.setColor(Color.WHITE);
+                               g2D.setXORMode(Color.RED);
+                               g2D.drawRect(rect.x, rect.y, rect.width, rect.height);
+                               dragged = false;
+                       }
+               }
+       }
+
+}