001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.plugins.streetside;
003
004import java.text.SimpleDateFormat;
005import java.util.Calendar;
006import java.util.Date;
007import java.util.Locale;
008
009import org.openstreetmap.josm.data.coor.LatLon;
010import org.openstreetmap.josm.plugins.streetside.utils.StreetsideProperties;
011
012/**
013 * Abstract superclass for all image objects. At the moment there are just 2,
014 * {@link StreetsideImportedImage} and {@link StreetsideImage}.
015 *
016 * @author nokutu
017 *
018 */
019public abstract class StreetsideAbstractImage implements Comparable<StreetsideAbstractImage> {
020        /**
021         * If two values for field cd differ by less than EPSILON both values are
022         * considered equal.
023         */
024        private static final float EPSILON = 1e-5f;
025
026        protected String id;
027
028        private long ne;
029  private long pr;
030
031
032        /** The time the image was captured, in Epoch format. */
033        protected long cd;
034        /** Sequence of pictures containing this object. */
035        private StreetsideSequence sequence;
036
037        /** Position of the picture. */
038        protected LatLon latLon;
039        /** Direction of the picture. */
040        protected double he;
041        /** Temporal position of the picture until it is uploaded. */
042        private LatLon tempLatLon;
043        /**
044         * When the object is being dragged in the map, the temporal position is stored
045         * here.
046         */
047        private LatLon movingLatLon;
048        /** Temporal direction of the picture until it is uploaded */
049        private double tempHe;
050        /**
051         * When the object direction is being moved in the map, the temporal direction
052         * is stored here
053         */
054        protected double movingHe;
055        /** Whether the image must be drown in the map or not */
056        private boolean visible;
057
058        /**
059         * Creates a new object in the given position and with the given direction.
060         * {@link LatLon}
061         *
062         * @param id - the Streetside image id
063         *
064         * @param latLon
065         *            The latitude and longitude of the image.
066         * @param he
067         *            The direction of the picture (0 means north im Mapillary
068         *            camera direction is not yet supported in the Streetside plugin).
069         */
070        protected StreetsideAbstractImage(final String id, final LatLon latLon, final double he) {
071                this.id = id;
072                this.latLon = latLon;
073                tempLatLon = this.latLon;
074                movingLatLon = this.latLon;
075                this.he = he;
076                tempHe = he;
077                movingHe = he;
078                visible = true;
079        }
080
081        /**
082         * Creates a new object with the given id.
083         *
084         * @param id - the image id (All images require ids in Streetside)
085         */
086        protected StreetsideAbstractImage(final String id) {
087                this.id = id;
088
089                visible = true;
090        }
091
092        /**
093         * @return the id
094         */
095        public String getId() {
096                return id;
097        }
098
099        /**
100         * @param id
101         *            the id to set
102         */
103        public void setId(String id) {
104                this.id = id;
105        }
106
107        /**
108         * Returns the original direction towards the image has been taken.
109         *
110         * @return The direction of the image (0 means north and goes clockwise).
111         */
112        public double getHe() {
113                return he;
114        }
115
116        /**
117         * Returns the Epoch time when the image was captured.
118         *
119         * @return The long containing the Epoch time when the image was captured.
120         */
121        public long getCd() {
122                return cd;
123        }
124
125        /**
126         * Returns the date the picture was taken in DMY format.
127         *
128         * @return A String object containing the date when the picture was taken.
129         */
130        public String getDate() {
131                final StringBuilder format = new StringBuilder(26);
132                format.append("m/d/YYYY");
133                if (StreetsideProperties.DISPLAY_HOUR.get()) {
134                        if (StreetsideProperties.TIME_FORMAT_24.get()) {
135                                format.append(" - HH:mm:ss");
136                        } else {
137                                format.append(" - h:mm:ss a");
138                        }
139                }
140                return getDate(format.toString());
141        }
142
143        /**
144         * Returns the date the picture was taken in the given format.
145         *
146         * @param format
147         *            Format of the date. See {@link SimpleDateFormat}.
148         * @return A String containing the date the picture was taken using the given
149         *         format.
150         * @throws NullPointerException
151         *             if parameter format is <code>null</code>
152         */
153        public String getDate(String format) {
154                final Date date = new Date(getCd());
155                final SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.US);
156                formatter.setTimeZone(Calendar.getInstance().getTimeZone());
157                return formatter.format(date);
158        }
159
160        /**
161         * Returns a LatLon object containing the original coordinates of the object.
162         *
163         * @return The LatLon object with the position of the object.
164         */
165        public LatLon getLatLon() {
166                return latLon;
167        }
168
169        /**
170         * Returns the direction towards the image has been taken.
171         *
172         * @return The direction of the image (0 means north and goes clockwise).
173         */
174        public double getMovingHe() {
175                return movingHe;
176        }
177
178        /**
179         * Returns a LatLon object containing the current coordinates of the object.
180         * When you are dragging the image this changes.
181         *
182         * @return The LatLon object with the position of the object.
183         */
184        public LatLon getMovingLatLon() {
185                return movingLatLon;
186        }
187
188        /**
189         * Returns the sequence which contains this image. Never null.
190         *
191         * @return The StreetsideSequence object that contains this StreetsideImage.
192         */
193
194        public StreetsideSequence getSequence() {
195                synchronized (this) {
196                        if (sequence == null) {
197                                sequence = new StreetsideSequence();
198                                sequence.add(this);
199                        }
200                        return sequence;
201                }
202        }
203
204        /**
205         * Returns the last fixed direction of the object.
206         *
207         * @return The last fixed direction of the object. 0 means north.
208         */
209        public double getTempHe() {
210                return tempHe;
211        }
212
213        /**
214         * Returns the last fixed coordinates of the object.
215         *
216         * @return A LatLon object containing.
217         */
218        public LatLon getTempLatLon() {
219                return tempLatLon;
220        }
221
222        /**
223         * Returns whether the object has been modified or not.
224         *
225         * @return true if the object has been modified; false otherwise.
226         */
227        public boolean isModified() {
228                return !getMovingLatLon().equals(latLon) || Math.abs(getMovingHe() - cd) > EPSILON;
229        }
230
231        /**
232         * Returns whether the image is visible on the map or not.
233         *
234         * @return True if the image is visible; false otherwise.
235         */
236        public boolean isVisible() {
237                return visible;
238        }
239
240        /**
241         * Moves the image temporally to another position
242         *
243         * @param x
244         *            The movement of the image in longitude units.
245         * @param y
246         *            The movement of the image in latitude units.
247         */
248        public void move(final double x, final double y) {
249                movingLatLon = new LatLon(tempLatLon.getY() + y, tempLatLon.getX() + x);
250        }
251
252        /**
253         * If the StreetsideImage belongs to a StreetsideSequence, returns the next
254         * image in the sequence.
255         *
256         * @return The following StreetsideImage, or null if there is none.
257         */
258        public StreetsideAbstractImage next() {
259                synchronized (this) {
260                        return getSequence().next(this);
261                }
262        }
263
264        /**
265         * If the StreetsideImage belongs to a StreetsideSequence, returns the previous
266         * image in the sequence.
267         *
268         * @return The previous StreetsideImage, or null if there is none.
269         */
270        public StreetsideAbstractImage previous() {
271                synchronized (this) {
272                        return getSequence().previous(this);
273                }
274        }
275
276        public void setHe(final double he) {
277                this.he = he;
278        }
279
280        /**
281         * Sets the Epoch time when the picture was captured.
282         *
283         * @param cd
284         *            Epoch time when the image was captured.
285         */
286        public synchronized void setCd(final long cd) {
287                this.cd = cd;
288        }
289
290        public void setLatLon(final LatLon latLon) {
291                if (latLon != null) {
292                        this.latLon = latLon;
293                }
294        }
295
296        /**
297         * Sets the StreetsideSequence object which contains the StreetsideImage.
298         *
299         * @param sequence
300         *            The StreetsideSequence that contains the StreetsideImage.
301         * @throws IllegalArgumentException
302         *             if the image is not already part of the
303         *             {@link StreetsideSequence}. Call
304         *             {@link StreetsideSequence#add(StreetsideAbstractImage)} first.
305         */
306        public void setSequence(final StreetsideSequence sequence) {
307                synchronized (this) {
308                        if (sequence != null && !sequence.getImages().contains(this)) {
309                                throw new IllegalArgumentException();
310                        }
311                        this.sequence = sequence;
312                }
313        }
314
315        /**
316         * Set's whether the image should be visible on the map or not.
317         *
318         * @param visible
319         *            true if the image is set to be visible; false otherwise.
320         */
321        public void setVisible(final boolean visible) {
322                this.visible = visible;
323        }
324
325        /**
326         * Called when the mouse button is released, meaning that the picture has
327         * stopped being dragged, so the temporal values are saved.
328         */
329        public void stopMoving() {
330                tempLatLon = movingLatLon;
331                tempHe = movingHe;
332        }
333
334        /**
335         * Turns the image direction.
336         *
337         * @param ca
338         *            The angle the image is moving.
339         */
340        public void turn(final double ca) {
341                movingHe = tempHe + ca;
342        }
343
344        /**
345   * @return the ne
346   */
347  public long getNe() {
348    return ne;
349  }
350
351  /**
352   * @param ne the ne to set
353   */
354  public void setNe(long ne) {
355    this.ne = ne;
356  }
357
358  /**
359   * @return the pr
360   */
361  public long getPr() {
362    return pr;
363  }
364
365  /**
366   * @param pr the pr to set
367   */
368  public void setPr(long pr) {
369    this.pr = pr;
370  }
371
372
373}