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