Ignore:
Timestamp:
2013-03-22T22:18:26+01:00 (11 years ago)
Author:
zverik
Message:

ImageryID refactoring and javadocs

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  
    2020 * A layer that displays calibration geometry for an offset.
    2121 *
    22  * @author zverik
     22 * @author Zverik
     23 * @license WTFPL
    2324 */
    2425public class CalibrationLayer extends Layer {
     
    3435    }
    3536
     37    /**
     38     * Draw the calibration geometry with thin bright lines (or a crosshair
     39     * in case of a point).
     40     */
    3641    @Override
    3742    public void paint( Graphics2D g, MapView mv, Bounds box ) {
     
    7984    }
    8085
     86    /**
     87     * This is for determining a bounding box for the layer.
     88     */
    8189    @Override
    8290    public void visitBoundingBox( BoundingXYVisitor v ) {
     
    8593    }
    8694
     95    /**
     96     * A simple tooltip with geometry type, status and author.
     97     */
    8798    @Override
    8899    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();
    91102    }
    92103
     
    96107    }
    97108
     109    /**
     110     * This method returns standard actions plus "zoom to layer" and "change color".
     111     */
    98112    @Override
    99113    public Action[] getMenuEntries() {
     
    111125    }
    112126
     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     */
    113132    public void panToCenter() {
    114133        if( center == null ) {
     
    125144    }
    126145
     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     */
    127151    class SelectColorAction extends AbstractAction {
    128152        private Color c;
     
    140164    }
    141165
     166    /**
     167     * A simple icon with a colored rectangle.
     168     */
    142169    class SingleColorIcon implements Icon {
    143170        private Color color;
     
    162189    }
    163190
     191    /**
     192     * An action that calls {@link AutoScaleAction} which in turn
     193     * uses {@link #visitBoundingBox} to pan and zoom to the calibration geometry.
     194     */
    164195    class ZoomToLayerAction extends AbstractAction {
    165196        public ZoomToLayerAction() {
    166197            super(tr("Zoom to layer"));
     198            putValue(SMALL_ICON, ImageProvider.get("dialogs/autoscale/layer"));
    167199        }
    168200
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/CalibrationObject.java

    r29376 r29384  
    88
    99/**
     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.
    1013 *
    11  * @author zverik
     14 * @author Zverik
     15 * @license WTFPL
    1216 */
    1317public class CalibrationObject extends ImageryOffsetBase {
    1418    private LatLon[] geometry;
    1519
     20    /**
     21     * Initialize a calibration object from the array of nodes.
     22     */
    1623    public CalibrationObject(LatLon[] geometry) {
    1724        this.geometry = geometry;
    1825    }
    1926
     27    /**
     28     * Initialize a calibration object from OSM primitive.
     29     */
    2030    public CalibrationObject( OsmPrimitive p ) {
    2131        if( p instanceof Node )
     
    2939    }
    3040
     41    /**
     42     * Get an array of points for this geometry.
     43     */
    3144    public LatLon[] getGeometry() {
    3245        return geometry;
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/DeprecateOffsetAction.java

    r29381 r29384  
    1212
    1313/**
    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.
    1515 *
    16  * @author zverik
     16 * @author Zverik
     17 * @license WTFPL
    1718 */
    1819public class DeprecateOffsetAction extends AbstractAction {
     
    2021    private QuerySuccessListener listener;
    2122   
     23    /**
     24     * Initialize an action with an offset object.
     25     */
    2226    public DeprecateOffsetAction( ImageryOffsetBase offset ) {
    2327        super(tr("Deprecate Offset"));
     
    2731    }
    2832
     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     */
    2939    public void actionPerformed(ActionEvent e) {
    3040        if( Main.map == null || Main.map.mapView == null || !Main.map.isVisible() )
     
    4151    }
    4252
     53    /**
     54     * Installs a listener to process successful deprecation event.
     55     */
    4356    public void setListener( QuerySuccessListener listener ) {
    4457        this.listener = listener;
    4558    }
    4659
     60    /**
     61     * Deprecate the given offset.
     62     * @see #deprecateOffset(iodb.ImageryOffsetBase, iodb.QuerySuccessListener)
     63     */
    4764    public static void deprecateOffset( ImageryOffsetBase offset ) {
    4865        deprecateOffset(offset, null);
    4966    }
    5067
     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     */
    5172    public static void deprecateOffset( ImageryOffsetBase offset, QuerySuccessListener listener ) {
    5273        String userName = JosmUserIdentityManager.getInstance().getUserName();
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/GetImageryOffsetAction.java

    r29381 r29384  
    2020 * Download a list of imagery offsets for the current position, let user choose which one to use.
    2121 *
    22  * @author zverik
     22 * @author Zverik
     23 * @license WTFPL
    2324 */
    2425public class GetImageryOffsetAction extends JosmAction {
    2526   
     27    /**
     28     * Initialize the action. Sets "Ctrl+Alt+I" shortcut: the only shortcut in this plugin.
     29     */
    2630    public GetImageryOffsetAction() {
    2731        super(tr("Get Imagery Offset..."), "getoffset", tr("Download offsets for current imagery from a server"),
     
    3034    }
    3135
     36    /**
     37     * The action just executes {@link DownloadOffsetsTask}.
     38     */
    3239    public void actionPerformed(ActionEvent e) {
    3340        if( Main.map == null || Main.map.mapView == null || !Main.map.isVisible() )
     
    4451    }
    4552
     53    /**
     54     * This action is enabled when there's a map, mapView and one of the layers
     55     * is an imagery layer.
     56     */
    4657    @Override
    4758    protected void updateEnabledState() {
     
    5566    }
    5667   
    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 ) {
    5874        if( offsets.isEmpty() ) {
    5975            JOptionPane.showMessageDialog(Main.parent,
     
    6783    }
    6884
     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     */
    6990    class DownloadOffsetsTask extends SimpleOffsetQueryTask {
    7091        private ImageryLayer layer;
    7192        private List<ImageryOffsetBase> offsets;
    7293
     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         */
    73100        public DownloadOffsetsTask( LatLon center, ImageryLayer layer, String imagery ) {
    74101            super(null, tr("Loading imagery offsets..."));
     
    87114        }
    88115
     116        /**
     117         * Displays offset dialog on success.
     118         */
    89119        @Override
    90120        protected void afterFinish() {
    91121            if( !cancelled && offsets != null )
    92                 showOffsetDialog(offsets, layer);
     122                showOffsetDialog(offsets);
    93123        }
    94124       
     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         */
    95130        @Override
    96131        protected void processResponse( InputStream inp ) throws UploadException {
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/IODBReader.java

    r29376 r29384  
    1818
    1919/**
    20  * Parses the message from server. It expects XML in UTF-8 with several &lt;offset&gt; elements.
     20 * Parses the server response. It expects XML in UTF-8 with several &lt;offset&gt;
     21 * and &lt;calibration&gt; elements.
    2122 *
    22  * @author zverik
     23 * @author Zverik
     24 * @license WTFPL
    2325 */
    2426public class IODBReader {
     
    2628    private InputSource source;
    2729   
     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     */
    2863    private class Parser extends DefaultHandler {
    2964        private StringBuffer accumulator = new StringBuffer();
     
    3368        private SimpleDateFormat dateParser = new SimpleDateFormat("yyyy-MM-dd");
    3469
     70        /**
     71         * Initialize all fields.
     72         */
    3573        @Override
    3674        public void startDocument() throws SAXException {
     
    4078        }
    4179
     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         */
    4287        private LatLon parseLatLon(Attributes atts) {
    4388            return new LatLon(
     
    111156                        offsets.add(fields.constructObject());
    112157                    } catch( IllegalArgumentException ex ) {
     158                        // On one hand, we don't care, but this situation is one
     159                        // of those "it can never happen" cases.
    113160                        System.err.println(ex.getMessage());
    114161                    }
     
    119166    }
    120167   
    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     */
    139173    private class IOFields {
    140174        public int id;
     
    151185        public List<LatLon> geometry;
    152186
     187        /**
     188         * A constructor just calls {@link #clear()}.
     189         */
    153190        public IOFields() {
    154191            clear();
    155192        }
    156193       
     194        /**
     195         * Clear all fields to <tt>null</tt> and <tt>-1</tt>.
     196         */
    157197        public void clear() {
    158198            id = -1;
     
    171211        }
    172212
     213        /**
     214         * Creates an offset object from the fields. Also validates them, but not vigorously.
     215         * @return A new offset object.
     216         */
    173217        public ImageryOffsetBase constructObject() {
    174218            if( author == null || description == null || position == null || date == null )
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffset.java

    r29371 r29384  
    66
    77/**
    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.
    911 *
    10  * @author zverik
     12 * @author Zverik
     13 * @license WTFPL
    1114 */
    1215public class ImageryOffset extends ImageryOffsetBase {
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetBase.java

    r29376 r29384  
    77
    88/**
    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.
    1012 *
    11  * @author zverik
     13 * @author Zverik
     14 * @license WTFPL
    1215 */
    1316public class ImageryOffsetBase {
     
    2124    protected String abandonReason;
    2225   
     26    /**
     27     * Initialize object with the basic information. It's offset location, author, date
     28     * and description.
     29     */
    2330    public void setBasicInfo( LatLon position, String author, String description, Date date ) {
    2431        this.position = position;
     
    3744    }
    3845
     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     */
    3950    public void setDeprecated(Date abandonDate, String author, String reason) {
    4051        this.abandonDate = abandonDate;
     
    5566    }
    5667   
     68    /**
     69     * Check that {@link #getAbandonDate()} is not null. Note that
     70     * is doesn't say anything about abandonAuthor or abandonReason.
     71     */
    5772    public boolean isDeprecated() {
    5873        return abandonDate != null;
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetPlugin.java

    r29382 r29384  
    99
    1010/**
    11  * Add some actions to the imagery menu.
     11 * A plugin to request and store imagery offsets in the centralized database.
    1212 *
    13  * @author zverik
     13 * @author Zverik
     14 * @license WTFPL
    1415 */
    1516public class ImageryOffsetPlugin extends Plugin {
     
    1718    private StoreImageryOffsetAction storeAction;
    1819   
     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     */
    1926    public ImageryOffsetPlugin( PluginInformation info ) {
    2027        super(info);
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/ImageryOffsetTools.java

    r29382 r29384  
    66import org.openstreetmap.josm.data.coor.EastNorth;
    77import org.openstreetmap.josm.data.coor.LatLon;
    8 import org.openstreetmap.josm.data.imagery.ImageryInfo;
    98import org.openstreetmap.josm.data.projection.Projection;
    109import org.openstreetmap.josm.gui.MapView;
     
    1312
    1413/**
    15  * Some common static methods for querying imagery layers.
     14 * Some common static methods for querying and processing imagery layers.
    1615 *
    17  * @author zverik
     16 * @author Zverik
     17 * @license WTFPL
    1818 */
    1919public class ImageryOffsetTools {
     20    /**
     21     * A title for all dialogs created in this plugin.
     22     */
    2023    public static final String DIALOG_TITLE = tr("Imagery Offset");
    2124   
     25    /**
     26     * Returns the topmost visible imagery layer.
     27     * @return the layer, or null if it hasn't been found.
     28     */
    2229    public static ImageryLayer getTopImageryLayer() {
    2330        if( Main.map == null || Main.map.mapView == null )
     
    3239    }
    3340   
     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     */
    3445    public static LatLon getMapCenter() {
    3546        Projection proj = Main.getProjection();
     
    3849    }
    3950   
     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     */
    4058    public static LatLon getLayerOffset( ImageryLayer layer, LatLon center ) {
    4159        Projection proj = Main.getProjection();
     
    4664    }
    4765   
     66    /**
     67     * Applies the offset to the imagery layer.
     68     * @see #calculateOffset(iodb.ImageryOffset)
     69     * @see #getLayerOffset
     70     */
    4871    public static void applyLayerOffset( ImageryLayer layer, ImageryOffset offset ) {
    4972        double[] dxy = calculateOffset(offset);
     
    5376    /**
    5477     * Calculate dx and dy for imagery offset.
    55      * @return [dx, dy]
     78     * @return An array of [dx, dy].
     79     * @see #applyLayerOffset
    5680     */
    5781    public static double[] calculateOffset( ImageryOffset offset ) {
     
    6286    }
    6387   
     88    /**
     89     * Generate unique imagery identifier based on its type and URL.
     90     * @param layer imagery layer.
     91     * @return imagery id.
     92     */
    6493    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    }
    7597
    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 protocol
    84         int i = url.indexOf("://");
    85         url = url.substring(i + 3);
    86 
    87         // Split URL into address and query string
    88         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 map
    96         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 layers
    105             if( isWMS && removeWMSParams.contains(kv[0]) )
    106                 continue;
    107             // TMS: skip parameters with variable values
    108             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 parameters
    114         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 parts
    125         url = url.replaceAll("\\/\\{[^}]+\\}(?:\\.\\w+)?", "");
    126         // TMS: remove variable parts
    127         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    
    13798    // Following three methods were snatched from TMSLayer
    13899    private static double latToTileY(double lat, int zoom) {
     
    169130    }
    170131
    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     */
    188135    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);
    190137        if( d < 0.095 )  return formatDistance(d * 100,  tr("cm"), true );
    191138        if( d < 0.95 )   return formatDistance(d * 100,  tr("cm"), false);
     
    196143    }
    197144
     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     */
    198152    private static String formatDistance( double d, String si, boolean floating ) {
    199153        return MessageFormat.format(floating ? "{0,number,0.0} {1}" : "{0,number,0} {1}", d, si);
    200154    }
    201 
    202     public static String getServerURL() {
    203         return Main.pref.get("iodb.server.url", "http://offsets.textual.ru/");
    204     }
    205155}
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetDialog.java

    r29382 r29384  
    55import java.awt.event.ActionListener;
    66import java.awt.event.KeyEvent;
     7import java.io.IOException;
     8import java.net.HttpURLConnection;
     9import java.net.URL;
    710import java.util.*;
    811import java.util.List;
     
    1720import org.openstreetmap.josm.gui.layer.ImageryLayer;
    1821import org.openstreetmap.josm.gui.layer.MapViewPaintable;
     22import org.openstreetmap.josm.tools.*;
    1923import static org.openstreetmap.josm.tools.I18n.tr;
    20 import org.openstreetmap.josm.tools.ImageProvider;
    21 import org.openstreetmap.josm.tools.OpenBrowser;
    2224
    2325/**
    2426 * The dialog which presents a choice between imagery align options.
    2527 *
    26  * @author zverik
     28 * @author Zverik
     29 * @license WTFPL
    2730 */
    2831public class OffsetDialog extends JDialog implements ActionListener, NavigatableComponent.ZoomChangeListener, MapViewPaintable {
     
    3033    protected static final String PREF_DEPRECATED = "iodb.show.deprecated";
    3134    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;
    3342
    3443    private List<ImageryOffsetBase> offsets;
     
    3645    private JPanel buttonPanel;
    3746
     47    /**
     48     * Initialize the dialog and install listeners.
     49     * @param offsets The list of offset to choose from.
     50     */
    3851    public OffsetDialog( List<ImageryOffsetBase> offsets ) {
    3952        super(JOptionPane.getFrameForComponent(Main.parent), ImageryOffsetTools.DIALOG_TITLE,
     
    5063    }
    5164   
     65    /**
     66     * Creates the GUI.
     67     */
    5268    private void prepareDialog() {
    5369        updateButtonPanel();
     
    89105    }
    90106
     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     */
    91111    private void updateButtonPanel() {
    92112        List<ImageryOffsetBase> filteredOffsets = filterOffsets();
     
    112132    }
    113133
     134    /**
     135     * Make a filtered offset list out of the full one. Takes into
     136     * account both checkboxes.
     137     */
    114138    private List<ImageryOffsetBase> filterOffsets() {
    115139        boolean showCalibration = Main.pref.getBoolean(PREF_CALIBRATION, true);
     
    128152    }
    129153
     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     */
    130158    public void zoomChanged() {
    131159        for( Component c : buttonPanel.getComponents() ) {
     
    136164    }
    137165
     166    /**
     167     * Draw dots on the map where offsets are located. I doubt it has practical
     168     * value, but looks nice.
     169     */
    138170    public void paint( Graphics2D g, MapView mv, Bounds bbox ) {
    139171        if( offsets == null )
     
    152184    }
    153185   
     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     */
    154193    public ImageryOffsetBase showDialog() {
    155194        // todo: add a temporary layer showing all offsets
     
    164203    }
    165204
     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     */
    166233    public void applyOffset() {
    167234        if( selectedOffset instanceof ImageryOffset ) {
     
    191258    }
    192259
    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     */
    208263    private class DeprecateOffsetListener implements QuerySuccessListener {
    209264        ImageryOffsetBase offset;
    210265
     266        /**
     267         * Initialize the listener with an offset.
     268         */
    211269        public DeprecateOffsetListener( ImageryOffsetBase offset ) {
    212270            this.offset = offset;
    213271        }
    214272
     273        /**
     274         * Remove the deprecated offset from the offsets list. Then rebuild the button panel.
     275         */
    215276        public void queryPassed() {
    216277            offset.setDeprecated(new Date(), JosmUserIdentityManager.getInstance().getUserName(), "");
     
    219280    }
    220281
     282    /**
     283     * Opens a web browser with the wiki page in user's language.
     284     */
    221285    class HelpAction extends AbstractAction {
    222286
     
    227291
    228292        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();
    230295            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            }
    232307            OpenBrowser.displayUrl(base + lang + page);
    233308        }
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetDialogButton.java

    r29382 r29384  
    55import javax.swing.ImageIcon;
    66import javax.swing.JButton;
     7import org.openstreetmap.josm.Main;
     8import org.openstreetmap.josm.data.coor.EastNorth;
     9import org.openstreetmap.josm.data.coor.LatLon;
     10import org.openstreetmap.josm.data.projection.Projection;
    711import org.openstreetmap.josm.gui.layer.ImageryLayer;
    812import org.openstreetmap.josm.tools.ImageProvider;
    913
    1014/**
    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.
    1217 *
    13  * @author zverik
     18 * @author Zverik
     19 * @license WTFPL
    1420 */
    1521public class OffsetDialogButton extends JButton {
     
    2026    private double direction;
    2127
     28    /**
     29     * Initialize the button with an offset. Calculated all relevant values.
     30     * @param offset An offset to display on the button.
     31     */
    2232    public OffsetDialogButton( ImageryOffsetBase offset ) {
    2333        super();
     
    4959    }
    5060
     61    /**
     62     * Returns the offset associated with this button.
     63     */
    5164    public ImageryOffsetBase getOffset() {
    5265        return offset;
     
    6982    }
    7083
     84    /**
     85     * Calculates length and direction for two points in the imagery offset object.
     86     * @see #getLengthAndDirection(iodb.ImageryOffset, double, double)
     87     */
    7188    private double[] getLengthAndDirection( ImageryOffset offset ) {
    7289        ImageryLayer layer = ImageryOffsetTools.getTopImageryLayer();
    7390        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]);
    7592    }
    7693
     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     */
    77117    class OffsetIcon implements Icon {
    78118        private boolean isDeprecated;
     
    82122        private ImageIcon background;
    83123
     124        /**
     125         * Initialize the icon with an offset object. Calculates length and direction
     126         * of an arrow if they're needed.
     127         */
    84128        public OffsetIcon( ImageryOffsetBase offset ) {
    85129            isDeprecated = offset.isDeprecated();
     
    89133                ImageryLayer layer = ImageryOffsetTools.getTopImageryLayer();
    90134                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]);
    92136                length = ld[0];
    93137                direction = ld[1];
     
    97141        }
    98142
     143        /**
     144         * Paints the base image and adds to it according to the offset.
     145         */
    99146        public void paintIcon( Component comp, Graphics g, int x, int y ) {
    100147            background.paintIcon(comp, g, x, y);
     
    110157                } else {
    111158                    // draw an arrow
    112                     double arrowLength = length < 5 ? getIconWidth() / 2 - 1 : getIconWidth() - 4;
     159                    double arrowLength = length < 10 ? getIconWidth() / 2 - 1 : getIconWidth() - 4;
    113160                    g2.setStroke(new BasicStroke(2));
    114161                    int dx = (int)Math.round(Math.sin(direction) * arrowLength / 2);
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/OffsetInfoAction.java

    r29382 r29384  
    1010
    1111/**
    12  * Download a list of imagery offsets for the current position, let user choose which one to use.
     12 * Display an information box for an offset.
    1313 *
    14  * @author zverik
     14 * @author Zverik
     15 * @license WTFPL
    1516 */
    1617public 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");
    1819
    1920    private Object info;
    2021   
     22    /**
     23     * Initializes the action with an offset object.
     24     * Calls {@link #getInformationObject(iodb.ImageryOffsetBase)}.
     25     */
    2126    public OffsetInfoAction( ImageryOffsetBase offset ) {
    2227        super(tr("Offset Information"));
     
    2732    }
    2833
     34    /**
     35     * Shows a dialog with the pre-constructed message.
     36     */
    2937    public void actionPerformed(ActionEvent e) {
    3038        JOptionPane.showMessageDialog(Main.parent, info, ImageryOffsetTools.DIALOG_TITLE, JOptionPane.PLAIN_MESSAGE);
    3139    }
    3240
     41    /**
     42     * Constructs a string with all information about the given offset.
     43     */
    3344    public static Object getInformationObject( ImageryOffsetBase offset ) {
    3445        StringBuilder sb = new StringBuilder();
     
    6172    }
    6273
     74    /**
     75     * Explains a calibration object geometry type: whether is's a point,
     76     * a path or a polygon.
     77     */
    6378    public static String getGeometryType( CalibrationObject obj ) {
    6479        if( obj.getGeometry() == null )
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/QuerySuccessListener.java

    r29381 r29384  
    44 * A listener for {@link SimpleOffsetQueryTask}.
    55 *
    6  * @author zverik
     6 * @author Zverik
     7 * @license WTFPL
    78 */
    89public interface QuerySuccessListener {
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/SimpleOffsetQueryTask.java

    r29380 r29384  
    99import org.openstreetmap.josm.Main;
    1010import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    11 import org.openstreetmap.josm.io.OsmTransferException;
    12 import org.xml.sax.SAXException;
    1311import static org.openstreetmap.josm.tools.I18n.tr;
    1412
    1513/**
     14 * A task to query the imagery offset server and process the response.
    1615 *
    17  * @author zverik
     16 * @author Zverik
     17 * @license WTFPL
    1818 */
    1919class SimpleOffsetQueryTask extends PleaseWaitRunnable {
     
    2424    private QuerySuccessListener listener;
    2525
     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     */
    2631    public SimpleOffsetQueryTask( String query, String title ) {
    2732        super(tr("Uploading"));
     
    3136    }
    3237
     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     */
    3343    public void setQuery( String query ) {
    3444        this.query = query;
    3545    }
    3646
     47    /**
     48     * Install a listener for successful responses. There can be only one.
     49     */
    3750    public void setListener( QuerySuccessListener listener ) {
    3851        this.listener = listener;
    3952    }
    4053
     54    /**
     55     * Remove a listener for successful responses.
     56     */
    4157    public void removeListener() {
    4258        this.listener = null;
    4359    }
    4460
     61    /**
     62     * The main method: calls {@link #doQuery(java.lang.String)} and processes exceptions.
     63     */
    4564    @Override
    46     protected void realRun() throws SAXException, IOException, OsmTransferException {
     65    protected void realRun() {
    4766        getProgressMonitor().indeterminateSubTask(title);
    4867        try {
     
    5675    }
    5776
     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     */
    5884    private void doQuery( String query ) throws UploadException, IOException {
    5985        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
    6289            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
    6390            connection.connect();
     
    79106    }
    80107
     108    /**
     109     * Doesn't actually cancel, just raises a flag.
     110     */
    81111    @Override
    82112    protected void cancel() {
     
    84114    }
    85115
     116    /**
     117     * Is called after {@link #realRun()}. Either displays an error message
     118     * or notifies a listener of success.
     119     */
    86120    @Override
    87121    protected void finish() {
     
    93127    }
    94128
     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     */
    95134    protected void processResponse( InputStream inp ) throws UploadException {
    96135        String response = "";
     
    110149    }
    111150
     151    /**
     152     * A placeholder exception for error messages.
     153     */
    112154    public static class UploadException extends Exception {
    113155        public UploadException( String message ) {
  • applications/editors/josm/plugins/imagery_offset_db/src/iodb/StoreImageryOffsetAction.java

    r29382 r29384  
    1515
    1616/**
    17  * Upload the current imagery offset or an calibration object information.
     17 * Upload the current imagery offset or an calibration geometry information.
    1818 *
    19  * @author zverik
     19 * @author Zverik
     20 * @license WTFPL
    2021 */
    2122public class StoreImageryOffsetAction extends JosmAction {
    2223
     24    /**
     25     * Initializes the action.
     26     */
    2327    public StoreImageryOffsetAction() {
    2428        super(tr("Store Imagery Offset..."), "storeoffset",
    2529                tr("Upload an offset for current imagery (or calibration object information) to a server"),
    26                 null, false);
     30                null, true);
    2731    }
    2832
     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     */
    2941    public void actionPerformed(ActionEvent e) {
    3042        if( Main.map == null || Main.map.mapView == null )
     
    3648
    3749        String userName = JosmUserIdentityManager.getInstance().getUserName();
    38         if( userName == null ) {
     50        if( userName == null || userName.length() == 0 ) {
    3951            JOptionPane.showMessageDialog(Main.parent, tr("To store imagery offsets you must be a registered OSM user."), ImageryOffsetTools.DIALOG_TITLE, JOptionPane.ERROR_MESSAGE);
    4052            return;
     
    108120    }
    109121
     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     */
    110128    public static String queryDescription( Object message ) {
    111129        String reason = null;
     
    129147    }
    130148
     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     */
    131153    @Override
    132154    protected void updateEnabledState() {
Note: See TracChangeset for help on using the changeset viewer.