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