diff --git a/src/main/java/NTNU/IDATT1002/controllers/ImageMapFactory.java b/src/main/java/NTNU/IDATT1002/controllers/ImageMapFactory.java index a1c4c11af6426ae95dc08ab4a73954534d953c58..ea0da30d218de7b4773434fb95e51e7722d8078a 100644 --- a/src/main/java/NTNU/IDATT1002/controllers/ImageMapFactory.java +++ b/src/main/java/NTNU/IDATT1002/controllers/ImageMapFactory.java @@ -2,55 +2,65 @@ package NTNU.IDATT1002.controllers; import NTNU.IDATT1002.models.GeoLocation; import NTNU.IDATT1002.models.Image; +import NTNU.IDATT1002.service.TagService; +import NTNU.IDATT1002.utils.MetadataStringFormatter; import com.lynden.gmapsfx.GoogleMapView; +import com.lynden.gmapsfx.javascript.event.UIEventType; import com.lynden.gmapsfx.javascript.object.*; +import netscape.javascript.JSObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; /** * Class ImageMapFactory. Factory for map creation with markers for given images and default options. - * Default center location is Berlin in order to center the full scale map onto a page. + * Default center location is Copenhagen in order to center the full scale map onto a page. * * @author Eirik Steira */ public class ImageMapFactory { - private static Logger logger = LoggerFactory.getLogger(ImageMapFactory.class); - - private ImageMapFactory() {} + private GoogleMap map; + private Logger logger = LoggerFactory.getLogger(ImageMapFactory.class); + private Map<LatLong, Image> latLongImageMapping = new HashMap<>(); + + public ImageMapFactory() { + } /** * Create a map from given {@link GoogleMapView} with default options. * - * @param googleMapView the map view to add the map to + * @param mapView the map view to add the map to * @return the {@link GoogleMap} created to enable further customization */ - public static GoogleMap createMap(GoogleMapView googleMapView) { + public GoogleMap createMap(GoogleMapView mapView) { MapOptions mapOptions = getMapOptions(); - return googleMapView.createMap(mapOptions); + map = mapView.createMap(mapOptions); + logger.info("[x] Map created"); + return map; } /** * Create default {@link MapOptions} to be applied to a map. Extend this for further marker customizations. - * The default center location is Berlin to get a look of the entire map when the zoom is set to fit the window. + * The default center location is Copenhagen to get a look of the entire map when the zoom is set to fit the window. * * @return the default map options */ - private static MapOptions getMapOptions() { - LatLong berlin = new LatLong(52.520008, 13.404954); + private MapOptions getMapOptions() { + LatLong copenhagen = new LatLong(55.676098, 12.568337); return new MapOptions() - .center(berlin) + .center(copenhagen) .mapType(MapTypeIdEnum.ROADMAP) .overviewMapControl(false) .panControl(false) - .rotateControl(false) - .scaleControl(false) .streetViewControl(false) - .zoomControl(false) + .zoomControl(true) .zoom(3); } @@ -60,7 +70,7 @@ public class ImageMapFactory { * @param images the list of images * @return a list of markers created from the images */ - public static List<Marker> createMarkers(List<Image> images) { + public List<Marker> createMarkers(List<Image> images) { List<LatLong> locations = getLatLongs(images); List<Marker> markers = getMarkers(locations); logger.info("[x] {} markers created", markers.size()); @@ -73,16 +83,27 @@ public class ImageMapFactory { * @param images the list of images * @return a list of {@link LatLong} */ - private static List<LatLong> getLatLongs(List<Image> images) { + private List<LatLong> getLatLongs(List<Image> images) { return images.stream() - .map(Image::getGeoLocation) - .filter(GeoLocation::hasLatLong) - .map(geoLocation -> { - double latitude = Double.parseDouble(geoLocation.getLatitude()); - double longitude = Double.parseDouble(geoLocation.getLongitude()); - return new LatLong(latitude, longitude); - }) - .collect(Collectors.toList()); + .filter(image -> image.getGeoLocation().hasLatLong()) + .map(image -> { + LatLong latLong = getLatLong(image); + latLongImageMapping.put(latLong, image); + return latLong; + }).collect(Collectors.toList()); + } + + /** + * Get a {@link LatLong} from a single image. + * + * @param image the image holding the {@link GeoLocation} + * @return the {@link LatLong} created + */ + private LatLong getLatLong(Image image) { + GeoLocation geoLocation = image.getGeoLocation(); + double latitude = Double.parseDouble(geoLocation.getLatitude()); + double longitude = Double.parseDouble(geoLocation.getLongitude()); + return new LatLong(latitude, longitude); } /** @@ -91,15 +112,58 @@ public class ImageMapFactory { * @param locations the list containing the locations * @return the list of markers created */ - private static List<Marker> getMarkers(List<LatLong> locations) { + private List<Marker> getMarkers(List<LatLong> locations) { return locations.stream() - .map(location -> { - MarkerOptions markerOptions = new MarkerOptions() - .position(location); - logger.info("[x] Marker created for location: {}", location); - return new Marker(markerOptions); - }) + .map(this::getMarker) .collect(Collectors.toList()); } + /** + * Create {@link Marker} for given location with map zoom and center on click event. + * + * @param location the location of the marker + * @return marker created + */ + private Marker getMarker(LatLong location) { + MarkerOptions markerOptions = new MarkerOptions() + .position(location) + .animation(Animation.DROP); + + logger.info("[x] Marker created for location: {}", location); + Marker marker = new Marker(markerOptions); + + InfoWindow infoWindow = getInfoWindow(location); + map.addUIEventHandler(marker, UIEventType.click, (JSObject obj) -> { + map.setZoom(10); + map.setCenter(location); + infoWindow.open(map, marker); + }); + + return marker; + } + + /** + * Get {@link InfoWindow} with default options to display the + * corresponding image data. + * + * @param location the location corresponding to an image + * @return the {@link InfoWindow} created + */ + private InfoWindow getInfoWindow(LatLong location) { + Image image = latLongImageMapping.get(location); + + String username = image.getUser().getUsername(); + String tags = TagService.getTagsAsString(image.getTags()); + Date uploadedAt = image.getUploadedAt(); + String metadata = MetadataStringFormatter.format(image.getMetadata(), "<br/>"); + + InfoWindowOptions infoWindowOptions = new InfoWindowOptions() + .content("<h3>Id: " + image.getId() + "</h3>" + + "<p><b>User:</b> " + username + "</p>" + + "<p><b>Tags:</b> " + tags + "</p>" + + "<p><b>Uploaded at:</b> " + uploadedAt + "</p>" + + "<p><b>Metadata:</b> <br/>" + metadata + "</p>"); + + return new InfoWindow(infoWindowOptions); + } } diff --git a/src/main/java/NTNU/IDATT1002/controllers/Map.java b/src/main/java/NTNU/IDATT1002/controllers/Map.java index f5057ae473335f78e13ead88b48907be845a2081..6aa64a2aa92178019ed787c0e1c842ad5d2d12f2 100644 --- a/src/main/java/NTNU/IDATT1002/controllers/Map.java +++ b/src/main/java/NTNU/IDATT1002/controllers/Map.java @@ -102,14 +102,15 @@ public class Map extends NavBarController implements Initializable, MapComponent */ @Override public void mapInitialized() { - googleMap = ImageMapFactory.createMap(mapView); + ImageMapFactory imageMapFactory = new ImageMapFactory(); + googleMap = imageMapFactory.createMap(mapView); Task<List<Image>> fetchImagesTask = getImageListTask(); executorService.submit(fetchImagesTask); fetchImagesTask.setOnSucceeded(workerStateEvent -> { List<Image> images = fetchImagesTask.getValue(); - List<Marker> markers = ImageMapFactory.createMarkers(images); + List<Marker> markers = imageMapFactory.createMarkers(images); googleMap.addMarkers(markers); }); } diff --git a/src/main/java/NTNU/IDATT1002/models/Image.java b/src/main/java/NTNU/IDATT1002/models/Image.java index 1fdb682dafedb98fabd7cc8f25bf0660a13fbcde..7e994784c545ab55f0ce0a3bc9125fcc212b1a97 100644 --- a/src/main/java/NTNU/IDATT1002/models/Image.java +++ b/src/main/java/NTNU/IDATT1002/models/Image.java @@ -116,11 +116,10 @@ public class Image { return uploadedAt; } - public String getPath() { - return path; + public User getUser() { + return user; } - /** * Add this image in the given album. * diff --git a/src/main/java/NTNU/IDATT1002/models/Metadata.java b/src/main/java/NTNU/IDATT1002/models/Metadata.java index a1fd351bf3dcfe07d8f1b0222c890a9d7d611450..e887712045ab2e5d29cd7805bc1274688dd25f15 100644 --- a/src/main/java/NTNU/IDATT1002/models/Metadata.java +++ b/src/main/java/NTNU/IDATT1002/models/Metadata.java @@ -61,6 +61,22 @@ public class Metadata { public Metadata() { } + public Metadata(Metadata metadata) { + this.metadataId = metadata.getMetadataId(); + this.image = metadata.getImage(); + this.geolocation = metadata.getGeoLocation(); + this.camera = metadata.getCamera(); + this.lens = metadata.getLens(); + this.aperture = metadata.getAperture(); + this.shutterSpeed = metadata.getShutterSpeed(); + this.ISO = metadata.getISO(); + this.focalLength = metadata.getFocalLength(); + this.fileType = metadata.getFileType(); + this.photoDate = metadata.getPhotoDate(); + this.fileSize = metadata.getFileSize(); + this.fileDimension = metadata.getFileDimension(); + } + public Long getMetadataId() { return metadataId; diff --git a/src/main/java/NTNU/IDATT1002/utils/MetadataStringFormatter.java b/src/main/java/NTNU/IDATT1002/utils/MetadataStringFormatter.java index 462392bd59a08ba3844e07ff7590d45c86d08df9..ce94455995c108e9b68b236a887fa2142b5d1860 100644 --- a/src/main/java/NTNU/IDATT1002/utils/MetadataStringFormatter.java +++ b/src/main/java/NTNU/IDATT1002/utils/MetadataStringFormatter.java @@ -1,5 +1,6 @@ package NTNU.IDATT1002.utils; +import NTNU.IDATT1002.models.GeoLocation; import NTNU.IDATT1002.models.Metadata; import org.apache.commons.text.WordUtils; import org.slf4j.Logger; @@ -38,14 +39,15 @@ public class MetadataStringFormatter { * @param delimiter the delimiter separating the fields * @return the formatted string */ - public static String format(Metadata metadata, char delimiter) { + public static String format(Metadata metadata, String delimiter) { + Metadata pureMetadata = new Metadata(metadata); + Stream<Field> fields = Arrays.stream(pureMetadata.getClass().getDeclaredFields()); - Stream<Field> fields = Arrays.stream(metadata.getClass().getDeclaredFields()); StringBuilder metadataString = new StringBuilder(); fields.filter(field -> include.contains(field.getName())) .forEach(field -> { - String formattedField = getFormattedField(metadata, delimiter, field); + String formattedField = getFormattedField(pureMetadata, delimiter, field); metadataString.append(formattedField); }); @@ -63,21 +65,17 @@ public class MetadataStringFormatter { * @return the formatted field as a string */ private static String getFormattedField(Metadata metadata, - char delimiter, + String delimiter, Field field) { field.setAccessible(true); String[] fieldNameSplitByUppercase = field.getName().split("(?=\\p{Upper}[a-z])"); String fieldNameTitle = String.join(" ", fieldNameSplitByUppercase); - StringBuilder fieldString = new StringBuilder(); - fieldString.append(WordUtils.capitalizeFully(fieldNameTitle)) - .append(": "); - - fieldString.append(getFieldValue(metadata, field)) - .append(delimiter); - - return fieldString.toString(); + return WordUtils.capitalizeFully(fieldNameTitle) + + ": " + + getFieldValue(metadata, field) + + delimiter; } /** @@ -88,14 +86,9 @@ public class MetadataStringFormatter { */ private static String getFieldValue(Metadata metadata, Field field) { StringBuilder fieldValueString = new StringBuilder(); + try { - if (field.get(metadata) == null) - fieldValueString.append("No ") - .append(field.getName()) - .append(" found."); - else - fieldValueString.append(field.get(metadata)); - + appendFieldValue(metadata, field, fieldValueString); } catch (IllegalAccessException e) { logger.error("[x] Failed to process field {}", field.getName(), e); } @@ -103,4 +96,34 @@ public class MetadataStringFormatter { return fieldValueString.toString(); } + /** + * Append given fields value from given metadata to given string builder. + * + * @param metadata the metadata object holding the data + * @param field the field to get the value from + * @param fieldValueString the StringBuilder to append to + * @throws IllegalAccessException if field does not exist or access is denied + */ + private static void appendFieldValue(Metadata metadata, Field field, StringBuilder fieldValueString) + throws IllegalAccessException { + Object fieldValue = field.get(metadata); + + if (fieldValue == null || fieldValue.equals("")) + fieldValueString.append("No ") + .append(field.getName()) + .append(" found."); + else if (field.getName().equals("geolocation")) + fieldValueString.append(getGeoLocationValue(metadata, field)); + else + fieldValueString.append(fieldValue); + } + + private static String getGeoLocationValue(Metadata metadata, Field field) throws IllegalAccessException { + GeoLocation geolocation = (GeoLocation) field.get(metadata); + return "latitude: " + + geolocation.getLatitude() + + ", longitude: " + + geolocation.getLatitude(); + } + } diff --git a/src/main/resources/NTNU/IDATT1002/map.fxml b/src/main/resources/NTNU/IDATT1002/map.fxml index 9c352273a7df656dbcf8ceef0f566ad97b5ce616..9a4c1dfd80012657d7145fc72eae3549ed1e114a 100644 --- a/src/main/resources/NTNU/IDATT1002/map.fxml +++ b/src/main/resources/NTNU/IDATT1002/map.fxml @@ -76,7 +76,7 @@ <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="797.0" prefWidth="1920.0" style="-fx-background-color: #888888;" VBox.vgrow="ALWAYS"> <children> - <GoogleMapView fx:id="mapView" prefHeight="750.0" prefWidth="761.0" AnchorPane.bottomAnchor="-185.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="-441.0" AnchorPane.topAnchor="0.0"/> + <GoogleMapView fx:id="mapView" prefHeight="750.0" prefWidth="761.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"/> </children> </AnchorPane> diff --git a/src/test/java/NTNU/IDATT1002/utils/MetadataStringFormatterTest.java b/src/test/java/NTNU/IDATT1002/utils/MetadataStringFormatterTest.java index ea13bcf6333f981db78212db34220bc9c8176a57..f1d39b0b693f88f72565444543ff634e2264bb56 100644 --- a/src/test/java/NTNU/IDATT1002/utils/MetadataStringFormatterTest.java +++ b/src/test/java/NTNU/IDATT1002/utils/MetadataStringFormatterTest.java @@ -27,7 +27,7 @@ class MetadataStringFormatterTest { @Disabled("Not able to solve CI test run failure on this test") @Test void testFormatReturnsFormattedString() { - String metadataString = MetadataStringFormatter.format(metadata, ' '); + String metadataString = MetadataStringFormatter.format(metadata, ""); assertEquals(metadataString.trim(), formattedMetadata.trim()); }