Changeset 29384 in osm for applications
- Timestamp:
- 2013-03-22T22:18:26+01:00 (11 years ago)
- Location:
- applications/editors/josm/plugins/imagery_offset_db/src/iodb
- Files:
-
- 1 added
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/imagery_offset_db/src/iodb/CalibrationLayer.java
r29377 r29384 20 20 * A layer that displays calibration geometry for an offset. 21 21 * 22 * @author zverik 22 * @author Zverik 23 * @license WTFPL 23 24 */ 24 25 public class CalibrationLayer extends Layer { … … 34 35 } 35 36 37 /** 38 * Draw the calibration geometry with thin bright lines (or a crosshair 39 * in case of a point). 40 */ 36 41 @Override 37 42 public void paint( Graphics2D g, MapView mv, Bounds box ) { … … 79 84 } 80 85 86 /** 87 * This is for determining a bounding box for the layer. 88 */ 81 89 @Override 82 90 public void visitBoundingBox( BoundingXYVisitor v ) { … … 85 93 } 86 94 95 /** 96 * A simple tooltip with geometry type, status and author. 97 */ 87 98 @Override 88 99 public String getToolTipText() { 89 return "A " + (obj.isDeprecated() ? "deprecated " : "") + "calibration " + OffsetInfoAction.getGeometryType(obj)90 + " by " + obj.getAuthor();100 return "A " + (obj.isDeprecated() ? "deprecated " : "") + "calibration " 101 + OffsetInfoAction.getGeometryType(obj) + " by " + obj.getAuthor(); 91 102 } 92 103 … … 96 107 } 97 108 109 /** 110 * This method returns standard actions plus "zoom to layer" and "change color". 111 */ 98 112 @Override 99 113 public Action[] getMenuEntries() { … … 111 125 } 112 126 127 /** 128 * This method pans to the geometry, preserving zoom. It is used 129 * from {@link GetImageryOffsetAction}, because {@link AutoScaleAction} 130 * doesn't have a relevant method. 131 */ 113 132 public void panToCenter() { 114 133 if( center == null ) { … … 125 144 } 126 145 146 /** 147 * An action to change a color of a geometry. The color 148 * is specified in the constuctor. See {@link #getMenuEntries()} for 149 * the list of enabled colors. 150 */ 127 151 class SelectColorAction extends AbstractAction { 128 152 private Color c; … … 140 164 } 141 165 166 /** 167 * A simple icon with a colored rectangle. 168 */ 142 169 class SingleColorIcon implements Icon { 143 170 private Color color; … … 162 189 } 163 190 191 /** 192 * An action that calls {@link AutoScaleAction} which in turn 193 * uses {@link #visitBoundingBox} to pan and zoom to the calibration geometry. 194 */ 164 195 class ZoomToLayerAction extends AbstractAction { 165 196 public ZoomToLayerAction() { 166 197 super(tr("Zoom to layer")); 198 putValue(SMALL_ICON, ImageProvider.get("dialogs/autoscale/layer")); 167 199 } 168 200 -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/CalibrationObject.java
r29376 r29384 8 8 9 9 /** 10 * A calibration geometry data type. It was called an object long ago, 11 * when it contained an information on an OSM object. I decided not to rename 12 * this class. 10 13 * 11 * @author zverik 14 * @author Zverik 15 * @license WTFPL 12 16 */ 13 17 public class CalibrationObject extends ImageryOffsetBase { 14 18 private LatLon[] geometry; 15 19 20 /** 21 * Initialize a calibration object from the array of nodes. 22 */ 16 23 public CalibrationObject(LatLon[] geometry) { 17 24 this.geometry = geometry; 18 25 } 19 26 27 /** 28 * Initialize a calibration object from OSM primitive. 29 */ 20 30 public CalibrationObject( OsmPrimitive p ) { 21 31 if( p instanceof Node ) … … 29 39 } 30 40 41 /** 42 * Get an array of points for this geometry. 43 */ 31 44 public LatLon[] getGeometry() { 32 45 return geometry; -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/DeprecateOffsetAction.java
r29381 r29384 12 12 13 13 /** 14 * Download a list of imagery offsets for the current position, let user choose which one to use.14 * A context-dependent action to deprecate an offset. 15 15 * 16 * @author zverik 16 * @author Zverik 17 * @license WTFPL 17 18 */ 18 19 public class DeprecateOffsetAction extends AbstractAction { … … 20 21 private QuerySuccessListener listener; 21 22 23 /** 24 * Initialize an action with an offset object. 25 */ 22 26 public DeprecateOffsetAction( ImageryOffsetBase offset ) { 23 27 super(tr("Deprecate Offset")); … … 27 31 } 28 32 33 /** 34 * Asks a user if they really want to deprecate an offset (since this 35 * action is virtually irreversible) and calls 36 * {@link #deprecateOffset(iodb.ImageryOffsetBase, iodb.QuerySuccessListener)} 37 * on a positive answer. 38 */ 29 39 public void actionPerformed(ActionEvent e) { 30 40 if( Main.map == null || Main.map.mapView == null || !Main.map.isVisible() ) … … 41 51 } 42 52 53 /** 54 * Installs a listener to process successful deprecation event. 55 */ 43 56 public void setListener( QuerySuccessListener listener ) { 44 57 this.listener = listener; 45 58 } 46 59 60 /** 61 * Deprecate the given offset. 62 * @see #deprecateOffset(iodb.ImageryOffsetBase, iodb.QuerySuccessListener) 63 */ 47 64 public static void deprecateOffset( ImageryOffsetBase offset ) { 48 65 deprecateOffset(offset, null); 49 66 } 50 67 68 /** 69 * Deprecate the given offset and call listener on success. Asks user the reason 70 * and executes {@link SimpleOffsetQueryTask} with a query to deprecate the offset. 71 */ 51 72 public static void deprecateOffset( ImageryOffsetBase offset, QuerySuccessListener listener ) { 52 73 String userName = JosmUserIdentityManager.getInstance().getUserName(); -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/GetImageryOffsetAction.java
r29381 r29384 20 20 * Download a list of imagery offsets for the current position, let user choose which one to use. 21 21 * 22 * @author zverik 22 * @author Zverik 23 * @license WTFPL 23 24 */ 24 25 public class GetImageryOffsetAction extends JosmAction { 25 26 27 /** 28 * Initialize the action. Sets "Ctrl+Alt+I" shortcut: the only shortcut in this plugin. 29 */ 26 30 public GetImageryOffsetAction() { 27 31 super(tr("Get Imagery Offset..."), "getoffset", tr("Download offsets for current imagery from a server"), … … 30 34 } 31 35 36 /** 37 * The action just executes {@link DownloadOffsetsTask}. 38 */ 32 39 public void actionPerformed(ActionEvent e) { 33 40 if( Main.map == null || Main.map.mapView == null || !Main.map.isVisible() ) … … 44 51 } 45 52 53 /** 54 * This action is enabled when there's a map, mapView and one of the layers 55 * is an imagery layer. 56 */ 46 57 @Override 47 58 protected void updateEnabledState() { … … 55 66 } 56 67 57 private void showOffsetDialog( List<ImageryOffsetBase> offsets, ImageryLayer layer ) { 68 /** 69 * Display a dialog for choosing between offsets. If there are no offsets in 70 * the list, displays the relevant message instead. 71 * @param offsets List of offset objects to choose from. 72 */ 73 private void showOffsetDialog( List<ImageryOffsetBase> offsets ) { 58 74 if( offsets.isEmpty() ) { 59 75 JOptionPane.showMessageDialog(Main.parent, … … 67 83 } 68 84 85 /** 86 * A task that downloads offsets for a given position and imagery layer, 87 * then parses resulting XML and calls 88 * {@link #showOffsetDialog(java.util.List)} on success. 89 */ 69 90 class DownloadOffsetsTask extends SimpleOffsetQueryTask { 70 91 private ImageryLayer layer; 71 92 private List<ImageryOffsetBase> offsets; 72 93 94 /** 95 * Initializes query object from the parameters. 96 * @param center A center point of a map view. 97 * @param layer The topmost imagery layer. 98 * @param imagery Imagery ID for the layer. 99 */ 73 100 public DownloadOffsetsTask( LatLon center, ImageryLayer layer, String imagery ) { 74 101 super(null, tr("Loading imagery offsets...")); … … 87 114 } 88 115 116 /** 117 * Displays offset dialog on success. 118 */ 89 119 @Override 90 120 protected void afterFinish() { 91 121 if( !cancelled && offsets != null ) 92 showOffsetDialog(offsets , layer);122 showOffsetDialog(offsets); 93 123 } 94 124 125 /** 126 * Parses the response with {@link IODBReader}. 127 * @param inp Response input stream. 128 * @throws iodb.SimpleOffsetQueryTask.UploadException Thrown on XML parsing error. 129 */ 95 130 @Override 96 131 protected void processResponse( InputStream inp ) throws UploadException { -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/IODBReader.java
r29376 r29384 18 18 19 19 /** 20 * Parses the message from server. It expects XML in UTF-8 with several <offset> elements. 20 * Parses the server response. It expects XML in UTF-8 with several <offset> 21 * and <calibration> elements. 21 22 * 22 * @author zverik 23 * @author Zverik 24 * @license WTFPL 23 25 */ 24 26 public class IODBReader { … … 26 28 private InputSource source; 27 29 30 /** 31 * Initializes the parser. This constructor creates an input source on the input 32 * stream, so it may throw an exception (though it's highly improbable). 33 * @param source An input stream with XML. 34 * @throws IOException Thrown when something's wrong with the stream. 35 */ 36 public IODBReader( InputStream source ) throws IOException { 37 this.source = new InputSource(UTFInputStreamReader.create(source, "UTF-8")); 38 this.offsets = new ArrayList<ImageryOffsetBase>(); 39 } 40 41 /** 42 * Parses the XML input stream. Creates {@link Parser} to do it. 43 * @return The list of offsets. 44 * @throws SAXException Thrown when the XML is malformed. 45 * @throws IOException Thrown when the input stream fails. 46 */ 47 public List<ImageryOffsetBase> parse() throws SAXException, IOException { 48 Parser parser = new Parser(); 49 try { 50 SAXParserFactory factory = SAXParserFactory.newInstance(); 51 factory.newSAXParser().parse(source, parser); 52 return offsets; 53 } catch( ParserConfigurationException e ) { 54 throw new SAXException(e); 55 } 56 } 57 58 /** 59 * The SAX handler for XML from the imagery offset server. 60 * Calls {@link IOFields#constructObject()} for every complete object 61 * and appends the result to offsets array. 62 */ 28 63 private class Parser extends DefaultHandler { 29 64 private StringBuffer accumulator = new StringBuffer(); … … 33 68 private SimpleDateFormat dateParser = new SimpleDateFormat("yyyy-MM-dd"); 34 69 70 /** 71 * Initialize all fields. 72 */ 35 73 @Override 36 74 public void startDocument() throws SAXException { … … 40 78 } 41 79 80 /** 81 * Parses latitude and longitude from tag attributes. 82 * It expects to find them in "lat" and "lon" attributes 83 * as decimal degrees. Note that it does not check whether 84 * the resulting object is valid: it may not be, especially 85 * for locations near the Poles and 180th meridian. 86 */ 42 87 private LatLon parseLatLon(Attributes atts) { 43 88 return new LatLon( … … 111 156 offsets.add(fields.constructObject()); 112 157 } catch( IllegalArgumentException ex ) { 158 // On one hand, we don't care, but this situation is one 159 // of those "it can never happen" cases. 113 160 System.err.println(ex.getMessage()); 114 161 } … … 119 166 } 120 167 121 122 public IODBReader( InputStream source ) throws IOException { 123 this.source = new InputSource(UTFInputStreamReader.create(source, "UTF-8")); 124 this.offsets = new ArrayList<ImageryOffsetBase>(); 125 } 126 127 public List<ImageryOffsetBase> parse() throws SAXException, IOException { 128 Parser parser = new Parser(); 129 try { 130 SAXParserFactory factory = SAXParserFactory.newInstance(); 131 factory.newSAXParser().parse(source, parser); 132 return offsets; 133 } catch (ParserConfigurationException e) { 134 e.printStackTrace(); 135 throw new SAXException(e); 136 } 137 } 138 168 /** 169 * An accumulator for parsed fields. When there's enough data, it can construct 170 * an offset object. All fields are public to deliver us from tons of getters 171 * and setters. 172 */ 139 173 private class IOFields { 140 174 public int id; … … 151 185 public List<LatLon> geometry; 152 186 187 /** 188 * A constructor just calls {@link #clear()}. 189 */ 153 190 public IOFields() { 154 191 clear(); 155 192 } 156 193 194 /** 195 * Clear all fields to <tt>null</tt> and <tt>-1</tt>. 196 */ 157 197 public void clear() { 158 198 id = -1; … … 171 211 } 172 212 213 /** 214 * Creates an offset object from the fields. Also validates them, but not vigorously. 215 * @return A new offset object. 216 */ 173 217 public ImageryOffsetBase constructObject() { 174 218 if( author == null || description == null || position == null || date == null ) -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffset.java
r29371 r29384 6 6 7 7 /** 8 * An offset. 8 * An imagery offset. Contains imagery identifier, zoom bracket and a location 9 * of the position point on the imagery layer. The offset is then calculated 10 * as a difference between the two. 9 11 * 10 * @author zverik 12 * @author Zverik 13 * @license WTFPL 11 14 */ 12 15 public class ImageryOffset extends ImageryOffsetBase { -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetBase.java
r29376 r29384 7 7 8 8 /** 9 * Stores one imagery offset record. 9 * Stores one offset record. It is the superclass for {@link ImageryOffset} 10 * and {@link CalibrationObject} classes and contains common fields 11 * like position, author and description. 10 12 * 11 * @author zverik 13 * @author Zverik 14 * @license WTFPL 12 15 */ 13 16 public class ImageryOffsetBase { … … 21 24 protected String abandonReason; 22 25 26 /** 27 * Initialize object with the basic information. It's offset location, author, date 28 * and description. 29 */ 23 30 public void setBasicInfo( LatLon position, String author, String description, Date date ) { 24 31 this.position = position; … … 37 44 } 38 45 46 /** 47 * Mark the offset as deprecated. Though there is no exact field for "isDeprecated", 48 * it is deduced from abandonDate, author and reason being not null. 49 */ 39 50 public void setDeprecated(Date abandonDate, String author, String reason) { 40 51 this.abandonDate = abandonDate; … … 55 66 } 56 67 68 /** 69 * Check that {@link #getAbandonDate()} is not null. Note that 70 * is doesn't say anything about abandonAuthor or abandonReason. 71 */ 57 72 public boolean isDeprecated() { 58 73 return abandonDate != null; -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetPlugin.java
r29382 r29384 9 9 10 10 /** 11 * A dd some actions to the imagery menu.11 * A plugin to request and store imagery offsets in the centralized database. 12 12 * 13 * @author zverik 13 * @author Zverik 14 * @license WTFPL 14 15 */ 15 16 public class ImageryOffsetPlugin extends Plugin { … … 17 18 private StoreImageryOffsetAction storeAction; 18 19 20 /** 21 * Add both actions to their own menu. This creates 22 * "Offset" menu, because "Imagery" is constantly rebuilt, 23 * losing all changes, and other menus are either too long already, 24 * or completely unsuitable for imagery offset actions. 25 */ 19 26 public ImageryOffsetPlugin( PluginInformation info ) { 20 27 super(info); -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetTools.java
r29382 r29384 6 6 import org.openstreetmap.josm.data.coor.EastNorth; 7 7 import org.openstreetmap.josm.data.coor.LatLon; 8 import org.openstreetmap.josm.data.imagery.ImageryInfo;9 8 import org.openstreetmap.josm.data.projection.Projection; 10 9 import org.openstreetmap.josm.gui.MapView; … … 13 12 14 13 /** 15 * Some common static methods for querying imagery layers.14 * Some common static methods for querying and processing imagery layers. 16 15 * 17 * @author zverik 16 * @author Zverik 17 * @license WTFPL 18 18 */ 19 19 public class ImageryOffsetTools { 20 /** 21 * A title for all dialogs created in this plugin. 22 */ 20 23 public static final String DIALOG_TITLE = tr("Imagery Offset"); 21 24 25 /** 26 * Returns the topmost visible imagery layer. 27 * @return the layer, or null if it hasn't been found. 28 */ 22 29 public static ImageryLayer getTopImageryLayer() { 23 30 if( Main.map == null || Main.map.mapView == null ) … … 32 39 } 33 40 41 /** 42 * Calculates the center of a visible map area. 43 * @return the center point, or (0; 0) if there's no map on the screen. 44 */ 34 45 public static LatLon getMapCenter() { 35 46 Projection proj = Main.getProjection(); … … 38 49 } 39 50 51 /** 52 * Calculates an imagery layer offset. 53 * @param center The center of a visible map area. 54 * @return Coordinates of a point on the imagery which correspond to the 55 * center point on the map. 56 * @see #applyLayerOffset 57 */ 40 58 public static LatLon getLayerOffset( ImageryLayer layer, LatLon center ) { 41 59 Projection proj = Main.getProjection(); … … 46 64 } 47 65 66 /** 67 * Applies the offset to the imagery layer. 68 * @see #calculateOffset(iodb.ImageryOffset) 69 * @see #getLayerOffset 70 */ 48 71 public static void applyLayerOffset( ImageryLayer layer, ImageryOffset offset ) { 49 72 double[] dxy = calculateOffset(offset); … … 53 76 /** 54 77 * Calculate dx and dy for imagery offset. 55 * @return [dx, dy] 78 * @return An array of [dx, dy]. 79 * @see #applyLayerOffset 56 80 */ 57 81 public static double[] calculateOffset( ImageryOffset offset ) { … … 62 86 } 63 87 88 /** 89 * Generate unique imagery identifier based on its type and URL. 90 * @param layer imagery layer. 91 * @return imagery id. 92 */ 64 93 public static String getImageryID( ImageryLayer layer ) { 65 if( layer == null ) 66 return null; 67 68 String url = layer.getInfo().getUrl(); 69 if( url == null ) 70 return null; 71 72 // predefined layers 73 if( layer.getInfo().getImageryType().equals(ImageryInfo.ImageryType.BING) || url.contains("tiles.virtualearth.net") ) 74 return "bing"; 94 return layer == null ? null : 95 ImageryIdGenerator.getImageryID(layer.getInfo().getUrl(), layer.getInfo().getImageryType()); 96 } 75 97 76 if( layer.getInfo().getImageryType().equals(ImageryInfo.ImageryType.SCANEX) && url.toLowerCase().equals("irs") )77 return "scanex_irs";78 79 boolean isWMS = layer.getInfo().getImageryType().equals(ImageryInfo.ImageryType.WMS);80 81 // System.out.println(url);82 83 // Remove protocol84 int i = url.indexOf("://");85 url = url.substring(i + 3);86 87 // Split URL into address and query string88 i = url.indexOf('?');89 String query = "";90 if( i > 0 ) {91 query = url.substring(i);92 url = url.substring(0, i);93 }94 95 // Parse query parameters into a sorted map96 final Set<String> removeWMSParams = new TreeSet<String>(Arrays.asList(new String[] {97 "srs", "width", "height", "bbox", "service", "request", "version", "format", "styles", "transparent"98 }));99 Map<String, String> qparams = new TreeMap<String, String>();100 String[] qparamsStr = query.length() > 1 ? query.substring(1).split("&") : new String[0];101 for( String param : qparamsStr ) {102 String[] kv = param.split("=");103 kv[0] = kv[0].toLowerCase();104 // WMS: if this is WMS, remove all parameters except map and layers105 if( isWMS && removeWMSParams.contains(kv[0]) )106 continue;107 // TMS: skip parameters with variable values108 if( kv.length > 1 && kv[1].indexOf('{') >= 0 && kv[1].indexOf('}') > 0 )109 continue;110 qparams.put(kv[0].toLowerCase(), kv.length > 1 ? kv[1] : null);111 }112 113 // Reconstruct query parameters114 StringBuilder sb = new StringBuilder();115 for( String qk : qparams.keySet() ) {116 if( sb.length() > 0 )117 sb.append('&');118 else if( query.length() > 0 )119 sb.append('?');120 sb.append(qk).append('=').append(qparams.get(qk));121 }122 query = sb.toString();123 124 // TMS: remove /{zoom} and /{y}.png parts125 url = url.replaceAll("\\/\\{[^}]+\\}(?:\\.\\w+)?", "");126 // TMS: remove variable parts127 url = url.replaceAll("\\{[^}]+\\}", "");128 while( url.contains("..") )129 url = url.replace("..", ".");130 if( url.startsWith(".") )131 url = url.substring(1);132 133 // System.out.println("-> " + url + query);134 return url + query;135 }136 137 98 // Following three methods were snatched from TMSLayer 138 99 private static double latToTileY(double lat, int zoom) { … … 169 130 } 170 131 171 public static double[] getLengthAndDirection( ImageryOffset offset ) { 172 return getLengthAndDirection(offset, 0.0, 0.0); 173 } 174 175 public static double[] getLengthAndDirection( ImageryOffset offset, double dx, double dy ) { 176 Projection proj = Main.getProjection(); 177 EastNorth pos = proj.latlon2eastNorth(offset.getPosition()); 178 LatLon correctedCenterLL = proj.eastNorth2latlon(pos.add(-dx, -dy)); 179 double length = correctedCenterLL.greatCircleDistance(offset.getImageryPos()); 180 double direction = length < 1e-2 ? 0.0 : correctedCenterLL.heading(offset.getImageryPos()); 181 // todo: north vs south. Meanwhile, let's fix this dirty: 182 // direction = Math.PI - direction; 183 if( direction < 0 ) 184 direction += Math.PI * 2; 185 return new double[] {length, direction}; 186 } 187 132 /** 133 * Converts distance in meters to a human-readable string. 134 */ 188 135 public static String formatDistance( double d ) { 189 if( d < 0.0095 ) return formatDistance(d * 1000, tr("mm"), false);136 if( d < 0.0095 ) return formatDistance(d * 1000, tr("mm"), true); 190 137 if( d < 0.095 ) return formatDistance(d * 100, tr("cm"), true ); 191 138 if( d < 0.95 ) return formatDistance(d * 100, tr("cm"), false); … … 196 143 } 197 144 145 /** 146 * Constructs a distance string. 147 * @param d Distance. 148 * @param si Units of measure for distance. 149 * @param floating Whether a floating point is needed. 150 * @return A formatted string. 151 */ 198 152 private static String formatDistance( double d, String si, boolean floating ) { 199 153 return MessageFormat.format(floating ? "{0,number,0.0} {1}" : "{0,number,0} {1}", d, si); 200 154 } 201 202 public static String getServerURL() {203 return Main.pref.get("iodb.server.url", "http://offsets.textual.ru/");204 }205 155 } -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetDialog.java
r29382 r29384 5 5 import java.awt.event.ActionListener; 6 6 import java.awt.event.KeyEvent; 7 import java.io.IOException; 8 import java.net.HttpURLConnection; 9 import java.net.URL; 7 10 import java.util.*; 8 11 import java.util.List; … … 17 20 import org.openstreetmap.josm.gui.layer.ImageryLayer; 18 21 import org.openstreetmap.josm.gui.layer.MapViewPaintable; 22 import org.openstreetmap.josm.tools.*; 19 23 import static org.openstreetmap.josm.tools.I18n.tr; 20 import org.openstreetmap.josm.tools.ImageProvider;21 import org.openstreetmap.josm.tools.OpenBrowser;22 24 23 25 /** 24 26 * The dialog which presents a choice between imagery align options. 25 27 * 26 * @author zverik 28 * @author Zverik 29 * @license WTFPL 27 30 */ 28 31 public class OffsetDialog extends JDialog implements ActionListener, NavigatableComponent.ZoomChangeListener, MapViewPaintable { … … 30 33 protected static final String PREF_DEPRECATED = "iodb.show.deprecated"; 31 34 private static final int MAX_OFFSETS = Main.main.pref.getInteger("iodb.max.offsets", 5); 32 private static final boolean MODAL = false; // modal does not work for executing actions 35 36 /** 37 * Whether to create a modal frame. It turns out, modal dialogs 38 * block swing worker thread, so offset deprecation, for example, takes 39 * place only after the dialog is closed. Very inconvenient. 40 */ 41 private static final boolean MODAL = false; 33 42 34 43 private List<ImageryOffsetBase> offsets; … … 36 45 private JPanel buttonPanel; 37 46 47 /** 48 * Initialize the dialog and install listeners. 49 * @param offsets The list of offset to choose from. 50 */ 38 51 public OffsetDialog( List<ImageryOffsetBase> offsets ) { 39 52 super(JOptionPane.getFrameForComponent(Main.parent), ImageryOffsetTools.DIALOG_TITLE, … … 50 63 } 51 64 65 /** 66 * Creates the GUI. 67 */ 52 68 private void prepareDialog() { 53 69 updateButtonPanel(); … … 89 105 } 90 106 107 /** 108 * As the name states, this method updates the button panel. It is called 109 * when a user clicks filtering checkboxes or deprecates an offset. 110 */ 91 111 private void updateButtonPanel() { 92 112 List<ImageryOffsetBase> filteredOffsets = filterOffsets(); … … 112 132 } 113 133 134 /** 135 * Make a filtered offset list out of the full one. Takes into 136 * account both checkboxes. 137 */ 114 138 private List<ImageryOffsetBase> filterOffsets() { 115 139 boolean showCalibration = Main.pref.getBoolean(PREF_CALIBRATION, true); … … 128 152 } 129 153 154 /** 155 * This listener method is called when a user pans or zooms the map. 156 * It does nothing, only passes the event to all displayed offset buttons. 157 */ 130 158 public void zoomChanged() { 131 159 for( Component c : buttonPanel.getComponents() ) { … … 136 164 } 137 165 166 /** 167 * Draw dots on the map where offsets are located. I doubt it has practical 168 * value, but looks nice. 169 */ 138 170 public void paint( Graphics2D g, MapView mv, Bounds bbox ) { 139 171 if( offsets == null ) … … 152 184 } 153 185 186 /** 187 * Display the dialog and get the return value is case of a modal frame. 188 * Creates GUI, install a temporary map layer (see {@link #paint} and 189 * shows the window. 190 * @return Null for a non-modal dialog, the selected offset 191 * (or, again, a null value) otherwise. 192 */ 154 193 public ImageryOffsetBase showDialog() { 155 194 // todo: add a temporary layer showing all offsets … … 164 203 } 165 204 205 /** 206 * This is a listener method for all buttons (except "Help"). 207 * It assigns a selected offset value and closes the dialog. 208 * If the dialog wasn't modal, it applies the offset immediately. 209 * Should it apply the offset either way? Probably. 210 * @see #applyOffset() 211 */ 212 public void actionPerformed( ActionEvent e ) { 213 if( e.getSource() instanceof OffsetDialogButton ) { 214 selectedOffset = ((OffsetDialogButton)e.getSource()).getOffset(); 215 } else 216 selectedOffset = null; 217 NavigatableComponent.removeZoomChangeListener(this); 218 setVisible(false); 219 if( !MODAL ) { 220 Main.map.mapView.removeTemporaryLayer(this); 221 Main.map.mapView.repaint(); 222 if( selectedOffset != null ) 223 applyOffset(); 224 } 225 } 226 227 228 /** 229 * Either applies imagery offset or adds a calibration geometry layer. 230 * If the offset for each type was chosen for the first time ever, 231 * it displays an informational message. 232 */ 166 233 public void applyOffset() { 167 234 if( selectedOffset instanceof ImageryOffset ) { … … 191 258 } 192 259 193 public void actionPerformed( ActionEvent e ) { 194 if( e.getSource() instanceof OffsetDialogButton ) { 195 selectedOffset = ((OffsetDialogButton)e.getSource()).getOffset(); 196 } else 197 selectedOffset = null; 198 NavigatableComponent.removeZoomChangeListener(this); 199 setVisible(false); 200 if( !MODAL ) { 201 Main.map.mapView.removeTemporaryLayer(this); 202 Main.map.mapView.repaint(); 203 if( selectedOffset != null ) 204 applyOffset(); 205 } 206 } 207 260 /** 261 * A lisntener for successful deprecations. 262 */ 208 263 private class DeprecateOffsetListener implements QuerySuccessListener { 209 264 ImageryOffsetBase offset; 210 265 266 /** 267 * Initialize the listener with an offset. 268 */ 211 269 public DeprecateOffsetListener( ImageryOffsetBase offset ) { 212 270 this.offset = offset; 213 271 } 214 272 273 /** 274 * Remove the deprecated offset from the offsets list. Then rebuild the button panel. 275 */ 215 276 public void queryPassed() { 216 277 offset.setDeprecated(new Date(), JosmUserIdentityManager.getInstance().getUserName(), ""); … … 219 280 } 220 281 282 /** 283 * Opens a web browser with the wiki page in user's language. 284 */ 221 285 class HelpAction extends AbstractAction { 222 286 … … 227 291 228 292 public void actionPerformed( ActionEvent e ) { 229 String base = "http://wiki.openstreetmap.org/wiki/"; 293 String base = Main.pref.get("url.openstreetmap-wiki", "http://wiki.openstreetmap.org/wiki/"); 294 String lang = LanguageInfo.getWikiLanguagePrefix(); 230 295 String page = "Imagery_Offset_Database"; 231 String lang = "RU:"; // todo: determine it 296 try { 297 // this logic was snatched from {@link org.openstreetmap.josm.gui.dialogs.properties.PropertiesDialog.HelpAction} 298 HttpURLConnection conn = Utils.openHttpConnection(new URL(base + lang + page)); 299 conn.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect", 10) * 1000); 300 if( conn.getResponseCode() != 200 ) { 301 conn.disconnect(); 302 lang = ""; 303 } 304 } catch( IOException ex ) { 305 lang = ""; 306 } 232 307 OpenBrowser.displayUrl(base + lang + page); 233 308 } -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetDialogButton.java
r29382 r29384 5 5 import javax.swing.ImageIcon; 6 6 import javax.swing.JButton; 7 import org.openstreetmap.josm.Main; 8 import org.openstreetmap.josm.data.coor.EastNorth; 9 import org.openstreetmap.josm.data.coor.LatLon; 10 import org.openstreetmap.josm.data.projection.Projection; 7 11 import org.openstreetmap.josm.gui.layer.ImageryLayer; 8 12 import org.openstreetmap.josm.tools.ImageProvider; 9 13 10 14 /** 11 * A button which shows offset information. 15 * A button which shows offset information. Must be spectacular, since it's the only 16 * non-JOptionPane GUI in the plugin. 12 17 * 13 * @author zverik 18 * @author Zverik 19 * @license WTFPL 14 20 */ 15 21 public class OffsetDialogButton extends JButton { … … 20 26 private double direction; 21 27 28 /** 29 * Initialize the button with an offset. Calculated all relevant values. 30 * @param offset An offset to display on the button. 31 */ 22 32 public OffsetDialogButton( ImageryOffsetBase offset ) { 23 33 super(); … … 49 59 } 50 60 61 /** 62 * Returns the offset associated with this button. 63 */ 51 64 public ImageryOffsetBase getOffset() { 52 65 return offset; … … 69 82 } 70 83 84 /** 85 * Calculates length and direction for two points in the imagery offset object. 86 * @see #getLengthAndDirection(iodb.ImageryOffset, double, double) 87 */ 71 88 private double[] getLengthAndDirection( ImageryOffset offset ) { 72 89 ImageryLayer layer = ImageryOffsetTools.getTopImageryLayer(); 73 90 double[] dxy = layer == null ? new double[] {0.0, 0.0} : new double[] {layer.getDx(), layer.getDy()}; 74 return ImageryOffsetTools.getLengthAndDirection((ImageryOffset)offset, dxy[0], dxy[1]);91 return getLengthAndDirection(offset, dxy[0], dxy[1]); 75 92 } 76 93 94 /** 95 * Calculates length and direction for two points in the imagery offset object 96 * taking into account an existing imagery layer offset. 97 * 98 * @see #getLengthAndDirection(iodb.ImageryOffset) 99 */ 100 public static double[] getLengthAndDirection( ImageryOffset offset, double dx, double dy ) { 101 Projection proj = Main.getProjection(); 102 EastNorth pos = proj.latlon2eastNorth(offset.getPosition()); 103 LatLon correctedCenterLL = proj.eastNorth2latlon(pos.add(-dx, -dy)); 104 double length = correctedCenterLL.greatCircleDistance(offset.getImageryPos()); 105 double direction = length < 1e-2 ? 0.0 : correctedCenterLL.heading(offset.getImageryPos()); 106 // todo: north vs south. Meanwhile, let's fix this dirty: 107 // direction = Math.PI - direction; 108 if( direction < 0 ) 109 direction += Math.PI * 2; 110 return new double[] {length, direction}; 111 } 112 113 /** 114 * An offset icon. Displays a plain calibration icon for a geometry 115 * and an arrow for an imagery offset. 116 */ 77 117 class OffsetIcon implements Icon { 78 118 private boolean isDeprecated; … … 82 122 private ImageIcon background; 83 123 124 /** 125 * Initialize the icon with an offset object. Calculates length and direction 126 * of an arrow if they're needed. 127 */ 84 128 public OffsetIcon( ImageryOffsetBase offset ) { 85 129 isDeprecated = offset.isDeprecated(); … … 89 133 ImageryLayer layer = ImageryOffsetTools.getTopImageryLayer(); 90 134 double[] dxy = layer == null ? new double[] {0.0, 0.0} : new double[] { layer.getDx(), layer.getDy() }; 91 double[] ld = ImageryOffsetTools.getLengthAndDirection((ImageryOffset)offset, dxy[0], dxy[1]);135 double[] ld = getLengthAndDirection((ImageryOffset)offset, dxy[0], dxy[1]); 92 136 length = ld[0]; 93 137 direction = ld[1]; … … 97 141 } 98 142 143 /** 144 * Paints the base image and adds to it according to the offset. 145 */ 99 146 public void paintIcon( Component comp, Graphics g, int x, int y ) { 100 147 background.paintIcon(comp, g, x, y); … … 110 157 } else { 111 158 // draw an arrow 112 double arrowLength = length < 5? getIconWidth() / 2 - 1 : getIconWidth() - 4;159 double arrowLength = length < 10 ? getIconWidth() / 2 - 1 : getIconWidth() - 4; 113 160 g2.setStroke(new BasicStroke(2)); 114 161 int dx = (int)Math.round(Math.sin(direction) * arrowLength / 2); -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetInfoAction.java
r29382 r29384 10 10 11 11 /** 12 * D ownload a list of imagery offsets for the current position, let user choose which one to use.12 * Display an information box for an offset. 13 13 * 14 * @author zverik 14 * @author Zverik 15 * @license WTFPL 15 16 */ 16 17 public class OffsetInfoAction extends AbstractAction { 17 public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(" yyyy-MM-dd");18 public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd MMMM yyyy"); 18 19 19 20 private Object info; 20 21 22 /** 23 * Initializes the action with an offset object. 24 * Calls {@link #getInformationObject(iodb.ImageryOffsetBase)}. 25 */ 21 26 public OffsetInfoAction( ImageryOffsetBase offset ) { 22 27 super(tr("Offset Information")); … … 27 32 } 28 33 34 /** 35 * Shows a dialog with the pre-constructed message. 36 */ 29 37 public void actionPerformed(ActionEvent e) { 30 38 JOptionPane.showMessageDialog(Main.parent, info, ImageryOffsetTools.DIALOG_TITLE, JOptionPane.PLAIN_MESSAGE); 31 39 } 32 40 41 /** 42 * Constructs a string with all information about the given offset. 43 */ 33 44 public static Object getInformationObject( ImageryOffsetBase offset ) { 34 45 StringBuilder sb = new StringBuilder(); … … 61 72 } 62 73 74 /** 75 * Explains a calibration object geometry type: whether is's a point, 76 * a path or a polygon. 77 */ 63 78 public static String getGeometryType( CalibrationObject obj ) { 64 79 if( obj.getGeometry() == null ) -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/QuerySuccessListener.java
r29381 r29384 4 4 * A listener for {@link SimpleOffsetQueryTask}. 5 5 * 6 * @author zverik 6 * @author Zverik 7 * @license WTFPL 7 8 */ 8 9 public interface QuerySuccessListener { -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/SimpleOffsetQueryTask.java
r29380 r29384 9 9 import org.openstreetmap.josm.Main; 10 10 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 11 import org.openstreetmap.josm.io.OsmTransferException;12 import org.xml.sax.SAXException;13 11 import static org.openstreetmap.josm.tools.I18n.tr; 14 12 15 13 /** 14 * A task to query the imagery offset server and process the response. 16 15 * 17 * @author zverik 16 * @author Zverik 17 * @license WTFPL 18 18 */ 19 19 class SimpleOffsetQueryTask extends PleaseWaitRunnable { … … 24 24 private QuerySuccessListener listener; 25 25 26 /** 27 * Initialize the task. 28 * @param query A query string, usually starting with an action word and a question mark. 29 * @param title A title for the progress monitor. 30 */ 26 31 public SimpleOffsetQueryTask( String query, String title ) { 27 32 super(tr("Uploading")); … … 31 36 } 32 37 38 /** 39 * In case a query was not specified when the object was constructed, 40 * it can be set with this method. 41 * @see #SimpleOffsetQueryTask(java.lang.String, java.lang.String) 42 */ 33 43 public void setQuery( String query ) { 34 44 this.query = query; 35 45 } 36 46 47 /** 48 * Install a listener for successful responses. There can be only one. 49 */ 37 50 public void setListener( QuerySuccessListener listener ) { 38 51 this.listener = listener; 39 52 } 40 53 54 /** 55 * Remove a listener for successful responses. 56 */ 41 57 public void removeListener() { 42 58 this.listener = null; 43 59 } 44 60 61 /** 62 * The main method: calls {@link #doQuery(java.lang.String)} and processes exceptions. 63 */ 45 64 @Override 46 protected void realRun() throws SAXException, IOException, OsmTransferException{65 protected void realRun() { 47 66 getProgressMonitor().indeterminateSubTask(title); 48 67 try { … … 56 75 } 57 76 77 /** 78 * Sends a request to the imagery offset server. Processes exceptions and 79 * return codes, calls {@link #processResponse(java.io.InputStream)} on success. 80 * @param query 81 * @throws iodb.SimpleOffsetQueryTask.UploadException 82 * @throws IOException 83 */ 58 84 private void doQuery( String query ) throws UploadException, IOException { 59 85 try { 60 URL url = new URL(ImageryOffsetTools.getServerURL() + query); 61 System.out.println("url=" + url); // todo: remove in release 86 String serverURL = Main.pref.get("iodb.server.url", "http://offsets.textual.ru/"); 87 URL url = new URL(serverURL + query); 88 Main.info("IODB URL = " + url); // todo: remove in release 62 89 HttpURLConnection connection = (HttpURLConnection)url.openConnection(); 63 90 connection.connect(); … … 79 106 } 80 107 108 /** 109 * Doesn't actually cancel, just raises a flag. 110 */ 81 111 @Override 82 112 protected void cancel() { … … 84 114 } 85 115 116 /** 117 * Is called after {@link #realRun()}. Either displays an error message 118 * or notifies a listener of success. 119 */ 86 120 @Override 87 121 protected void finish() { … … 93 127 } 94 128 129 /** 130 * Parse the response input stream and determine whether an operation 131 * was successful or not. 132 * @throws iodb.SimpleOffsetQueryTask.UploadException Thrown if an error message was found. 133 */ 95 134 protected void processResponse( InputStream inp ) throws UploadException { 96 135 String response = ""; … … 110 149 } 111 150 151 /** 152 * A placeholder exception for error messages. 153 */ 112 154 public static class UploadException extends Exception { 113 155 public UploadException( String message ) { -
applications/editors/josm/plugins/imagery_offset_db/src/iodb/StoreImageryOffsetAction.java
r29382 r29384 15 15 16 16 /** 17 * Upload the current imagery offset or an calibration objectinformation.17 * Upload the current imagery offset or an calibration geometry information. 18 18 * 19 * @author zverik 19 * @author Zverik 20 * @license WTFPL 20 21 */ 21 22 public class StoreImageryOffsetAction extends JosmAction { 22 23 24 /** 25 * Initializes the action. 26 */ 23 27 public StoreImageryOffsetAction() { 24 28 super(tr("Store Imagery Offset..."), "storeoffset", 25 29 tr("Upload an offset for current imagery (or calibration object information) to a server"), 26 null, false);30 null, true); 27 31 } 28 32 33 /** 34 * Asks user for description and calls the upload task. 35 * Also calculates a lot of things, checks whether the selected object 36 * is suitable for calibration geometry, constructs a map of query parameters etc. 37 * The only thing it doesn't do is check for the real user account name. 38 * This is because all server queries should be executed in workers, 39 * and we don't have one when a user name is needed. 40 */ 29 41 public void actionPerformed(ActionEvent e) { 30 42 if( Main.map == null || Main.map.mapView == null ) … … 36 48 37 49 String userName = JosmUserIdentityManager.getInstance().getUserName(); 38 if( userName == null ) {50 if( userName == null || userName.length() == 0 ) { 39 51 JOptionPane.showMessageDialog(Main.parent, tr("To store imagery offsets you must be a registered OSM user."), ImageryOffsetTools.DIALOG_TITLE, JOptionPane.ERROR_MESSAGE); 40 52 return; … … 108 120 } 109 121 122 /** 123 * Ask a user for a description / reason. This string should be 3 to 200 characters 124 * long, and the method enforces that. 125 * @param message A prompt for the input dialog. 126 * @return Either null or a string 3 to 200 letters long. 127 */ 110 128 public static String queryDescription( Object message ) { 111 129 String reason = null; … … 129 147 } 130 148 149 /** 150 * This action is enabled when there's a map and a visible imagery layer. 151 * Note that it doesn't require edit layer. 152 */ 131 153 @Override 132 154 protected void updateEnabledState() {
Note:
See TracChangeset
for help on using the changeset viewer.