Skip to content
Snippets Groups Projects
Commit a406bc1f authored by Eirik Steira's avatar Eirik Steira Committed by Mads Lundegaard
Browse files

Add Geocoding address search and autocompletion

Registered Google Maps API key in order to view the map

Fix find or create tag to search by name instead of id since non-existing tags wont have id

Fix recursion in Image, Album and Tag toString

Auto stash before checking out "HEAD"
Fix checkout commit issues

Improve debug logging in Map controller and add GoogleMapView to map view

Fix duplicate issues due to rebase

Log hibernate to logfile exclusively

Create ImageMapFactory to encourage reusability

Finished ImageMapFactory - static factory method

Add javadoc to ImageMapFactory

Made image fetching a background task in Map controller

Update javadoc in Map controller

Fix nullpointer exception when getting geolocation from image

Remove duplicate logging of exceptions in ExploreAlbums

Minor refactors, return new GeoLocation instead of null if image does not have metadata

Improve logging in ImageMapFactory and use longitude instead of latitude twice

Implement search on location/address with autocomplete

Add Google Places API for autocompletions

Add Google Maps Services instead of outdated Google Places API

Add Geocoding API search for adresses

Add VM options to export javafx.base/com.sun.javafx.event to org.controlsfx.controls

Finished location autocompletions related to the map

Add javadoc to Map controller
parent 5fe38201
No related branches found
No related tags found
1 merge request!165Weekly merge to Master
Showing
with 372 additions and 103 deletions
......@@ -10,6 +10,7 @@ bin/
.project
t14-test-images
log/
config.properties
# User-specific stuff
.idea/**/workspace.xml
......@@ -79,3 +80,5 @@ fabric.properties
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
config.properties
......@@ -4,6 +4,11 @@ stages:
- build
- test
# Cache downloaded dependencies and plugins between builds.
cache:
paths:
- /root/.m2/repository/
build:
stage: build
only:
......
# Test Database Connection Properties
DB_DRIVER=org.h2.Driver
DB_URL=jdbc:h2:mem:test_db;DB_CLOSE_DELAY=-1
DB_DIALECT=org.hibernate.dialect.H2Dialect
......@@ -124,7 +124,7 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
<li>java.lang.Object</li>
<li>
<ul class="inheritance">
<li>NTNU.IDATT1002.database.EntityManagerConfig</li>
<li>NTNU.IDATT1002.Config</li>
</ul>
</li>
</ul>
......
......@@ -38,11 +38,13 @@
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.2.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
......@@ -69,6 +71,12 @@
<version>13</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
<version>15-ea+2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
......@@ -115,6 +123,7 @@
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
......@@ -135,7 +144,7 @@
<version>1.56</version>
</dependency>
<!-- Google Maps API -->
<!-- GMapsFX -->
<dependency>
<groupId>com.lynden</groupId>
<artifactId>GMapsFX</artifactId>
......@@ -149,6 +158,20 @@
</exclusions>
</dependency>
<!-- Google Maps API Client -->
<dependency>
<groupId>com.google.maps</groupId>
<artifactId>google-maps-services</artifactId>
<version>0.11.0</version>
</dependency>
<!-- JavaFX Controls -->
<dependency>
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>11.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
......@@ -169,6 +192,10 @@
<configuration>
<mainClass>NTNU.IDATT1002.App</mainClass>
<executable>${java.home}\bin\java</executable>
<options>
<options>--add-exports</options>
<options>javafx.base/com.sun.javafx.event=org.controlsfx.controls</options>
</options>
</configuration>
</plugin>
......@@ -176,6 +203,11 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.0</version>
<configuration>
<systemPropertyVariables>
<config_properties>config.test.properties</config_properties>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
......
......@@ -26,6 +26,14 @@ public class App extends Application {
stage.show();
}
/**
* Shut down GeoApiContext on application stop to gracefully close the connection.
*/
@Override
public void stop(){
ex.getGeoApiContext().shutdown();
}
public static void setRoot(String fxml) throws IOException {
scene.setRoot(loadFXML(fxml));
}
......
package NTNU.IDATT1002;
import NTNU.IDATT1002.database.EntityManagerConfig;
import NTNU.IDATT1002.models.User;
import NTNU.IDATT1002.repository.UserRepository;
......@@ -28,7 +27,7 @@ public final class ApplicationState {
* Initiate properties and save an anonymous user once.
*/
static {
EntityManager entityManager = EntityManagerConfig.getEntityManager();
EntityManager entityManager = Config.getEntityManager();
userRepository = new UserRepository(entityManager);
}
......
package NTNU.IDATT1002.database;
package NTNU.IDATT1002;
import org.hibernate.cfg.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.*;
import java.util.*;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* Entity Manager Configuration Singleton for overriding hibernate persistence properties.
* Application Configuration Singleton for parsing hibernate persistence properties and Google Maps API key.
* Provides a single global access point to the applications entity manager.
*/
public class EntityManagerConfig {
public class Config {
private static EntityManager entityManager;
private static Map<String, Object> configOverrides = new HashMap<>();
private static Properties properties = Environment.getProperties();
private static Properties properties = loadProperties();
private static Logger logger = LoggerFactory.getLogger(Config.class);
private Config() {
}
private EntityManagerConfig() {
/**
* Load properties from configured properties file. Defaults to config.properties.
*
* @return the properties
*/
private static Properties loadProperties() {
String configFile = System.getProperty("config_properties", "config.properties");
Properties environmentProperties = new Properties();
try (InputStream input = new FileInputStream(configFile)) {
environmentProperties.load(input);
} catch (IOException ex) {
logger.error("[x] Failed to load config properties", ex);
}
return environmentProperties;
}
public static String getGoogleApiKey() {
return properties.getProperty("GOOGLE_API_KEY");
}
/**
......@@ -37,55 +67,21 @@ public class EntityManagerConfig {
* Parse properties and create the entity manager
*/
private static void configureEntityManager() {
if (shouldOverrideConfig())
parseConfigProperties();
parseConfigProperties();
createEntityManager();
}
private static boolean shouldOverrideConfig() {
return Boolean.parseBoolean((String) properties.get("OVERRIDE_DEFAULT_DB_CONFIG"));
}
/**
* Parse properties from resources/hibernate.properties and overrides found values.
* Parse properties and override config.
*/
private static void parseConfigProperties() {
Set<Object> envKeys = properties.keySet();
for (Object key : envKeys)
parseProperty(key);
configOverrides.put("javax.persistence.jdbc.driver", properties.getProperty("DB_DRIVER"));
configOverrides.put("javax.persistence.jdbc.url", properties.get("DB_URL"));
configOverrides.put("javax.persistence.jdbc.user", properties.get("DB_USER"));
configOverrides.put("javax.persistence.jdbc.password", properties.get("DB_PASSWORD"));
configOverrides.put("hibernate.dialect", properties.get("DB_DIALECT"));
}
/**
* Parse desired values from given key in properties and add them to the configuration overrides.
*
* @param key the key to parse
*/
private static void parseProperty(Object key) {
switch ((String) key) {
case "DB_DRIVER":
configOverrides.put("javax.persistence.jdbc.driver", properties.get(key));
break;
case "DB_URL":
configOverrides.put("javax.persistence.jdbc.url", properties.get(key));
break;
case "DB_USER":
configOverrides.put("javax.persistence.jdbc.user", properties.get(key));
break;
case "DB_PASSWORD":
configOverrides.put("javax.persistence.jdbc.password", properties.get(key));
break;
case "DB_DIALECT":
configOverrides.put("hibernate.dialect", properties.get(key));
break;
case "DB_HBM2DDL":
configOverrides.put("hibernate.hbm2ddl.auto", properties.get(key));
break;
default:
break;
}
}
/**
* Create an entity manager with configurations overrides.
......@@ -94,4 +90,5 @@ public class EntityManagerConfig {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("ImageApplication", configOverrides);
entityManager = emf.createEntityManager();
}
}
package NTNU.IDATT1002.controllers;
import NTNU.IDATT1002.database.EntityManagerConfig;
import NTNU.IDATT1002.Config;
import com.google.maps.GeoApiContext;
import javafx.application.HostServices;
import javax.persistence.EntityManager;
......@@ -14,8 +15,9 @@ import java.util.List;
*/
public class DataExchange {
private EntityManager entityManager;
private String apiKey;
public HostServices hostServices;
private GeoApiContext geoApiContext;
private String searchField;
private List<File> uploadedFiles;
private Long chosenAlbumId;
......@@ -23,16 +25,28 @@ public class DataExchange {
public DataExchange(){
searchField = "";
apiKey = Config.getGoogleApiKey();
geoApiContext = new GeoApiContext.Builder()
.apiKey(getApiKey())
.build();
}
public EntityManager getEntityManager() {
return EntityManagerConfig.getEntityManager();
return Config.getEntityManager();
}
public String getApiKey() {
return apiKey;
}
public HostServices getHostServices() {
return hostServices;
}
public GeoApiContext getGeoApiContext() {
return geoApiContext;
}
public List<File> getUploadedFiles() {
return uploadedFiles;
}
......@@ -49,10 +63,6 @@ public class DataExchange {
return chosenImg;
}
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public void setHostServices(HostServices hostServices) {
this.hostServices = hostServices;
}
......
package NTNU.IDATT1002.controllers;
import NTNU.IDATT1002.models.GeoLocation;
import NTNU.IDATT1002.models.Image;
import com.lynden.gmapsfx.GoogleMapView;
import com.lynden.gmapsfx.javascript.object.*;
......@@ -23,42 +24,14 @@ public class ImageMapFactory {
private ImageMapFactory() {}
/**
* Create a map from given {@link GoogleMapView} with default options and markers for images in given list.
* Create a map from given {@link GoogleMapView} with default options.
*
* @param googleMapView the map view to add the map to
* @param images the list of images to place on the map
* @return the {@link GoogleMap} created to enable further customization
*/
public static GoogleMap createMap(GoogleMapView googleMapView, List<Image> images) {
List<LatLong> locations = getLatLongs(images);
public static GoogleMap createMap(GoogleMapView googleMapView) {
MapOptions mapOptions = getMapOptions();
GoogleMap googleMap = googleMapView.createMap(mapOptions);
logger.info("[x] Google map created with {} locations", locations.size());
List<Marker> markers = getMarkers(locations);
googleMap.addMarkers(markers);
logger.info("[x] {} markers added", markers.size());
return googleMap;
}
/**
* Get latitude and longitude ({@link LatLong}) values for given images.
*
* @param images the list of images
* @return a list of {@link LatLong}
*/
private static List<LatLong> getLatLongs(List<Image> images) {
return images.stream()
.map(Image::getGeoLocation)
.map(geoLocation -> {
double latitude = Double.parseDouble(geoLocation.getLatitude());
double longitude = Double.parseDouble(geoLocation.getLongitude());
return new LatLong(latitude, longitude);
})
.collect(Collectors.toList());
return googleMapView.createMap(mapOptions);
}
/**
......@@ -81,6 +54,37 @@ public class ImageMapFactory {
.zoom(3);
}
/**
* Create markers from given images.
*
* @param images the list of images
* @return a list of markers created from the images
*/
public static List<Marker> createMarkers(List<Image> images) {
List<LatLong> locations = getLatLongs(images);
List<Marker> markers = getMarkers(locations);
logger.info("[x] {} markers created", markers.size());
return markers;
}
/**
* Get latitude and longitude ({@link LatLong}) values for given images.
*
* @param images the list of images
* @return a list of {@link LatLong}
*/
private static 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());
}
/**
* Create {@link Marker}s for each location in given list of locations.
*
......@@ -97,4 +101,5 @@ public class ImageMapFactory {
})
.collect(Collectors.toList());
}
}
......@@ -5,18 +5,29 @@ import NTNU.IDATT1002.models.Album;
import NTNU.IDATT1002.models.Image;
import NTNU.IDATT1002.service.AlbumService;
import NTNU.IDATT1002.service.ImageService;
import com.google.maps.*;
import com.google.maps.model.AutocompletePrediction;
import com.google.maps.model.GeocodingResult;
import com.google.maps.model.LatLng;
import com.lynden.gmapsfx.GoogleMapView;
import com.lynden.gmapsfx.MapComponentInitializedListener;
import com.lynden.gmapsfx.javascript.object.GoogleMap;
import java.util.Optional;
import com.lynden.gmapsfx.javascript.object.LatLong;
import com.lynden.gmapsfx.javascript.object.Marker;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import org.controlsfx.control.textfield.TextFields;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -25,9 +36,13 @@ import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Controls the buttons and changeable elements on map.fxml,
......@@ -48,11 +63,22 @@ public class Map implements Initializable, MapComponentInitializedListener {
@FXML
private GoogleMapView mapView;
private GoogleMap map;
@FXML
private TextField addressTextField;
private List<String> autoCompletions = new ArrayList<>();
private GoogleMap googleMap;
private GeoApiContext geoApiContext;
private StringProperty address = new SimpleStringProperty();
private ImageService imageService;
private AlbumService albumService;
private ExecutorService executorService = Executors.newCachedThreadPool();
private static Logger logger = LoggerFactory.getLogger(Map.class);
......@@ -63,51 +89,54 @@ public class Map implements Initializable, MapComponentInitializedListener {
}
/**
* Register the api key for Google Maps API and listen for map initialization
* in order to update the view once it has been initialized.
* Initialize {@link GoogleMapView} and {@link GeoApiContext} with required API key.
* Also add listener for map initialization and bind the address text field to a {@link SimpleStringProperty}.
*
* @param url
* @param rb
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
mapView.setKey("AIzaSyB_ox5XC8zYBS__aezKumB-KSgKGUjcRx4");
mapView.setKey(App.ex.getApiKey());
mapView.addMapInializedListener(this);
}
address.bind(addressTextField.textProperty());
geoApiContext = App.ex.getGeoApiContext();
}
/**
* Check is there is a current album in dataexchange first and uses images from album to display on map.
* If none is found it proceeds to get all images available and display on map
* Create map an fetch images from the appropriate task and add individual markers for images.
*/
@Override
public void mapInitialized() {
List<Image> albumImages = new ArrayList<>();
Long currentAlbumId;
googleMap = ImageMapFactory.createMap(mapView);
Task<List<Image>> fetchImagesTask = getImageListTask();
executorService.submit(fetchImagesTask);
if(App.ex.getChosenAlbumId() == null) {
executorService.submit(fetchImages);
fetchImagesTask.setOnSucceeded(workerStateEvent -> {
List<Image> images = fetchImagesTask.getValue();
List<Marker> markers = ImageMapFactory.createMarkers(images);
googleMap.addMarkers(markers);
});
}
fetchImages.setOnSucceeded(workerStateEvent -> {
List<Image> allImages = fetchImages.getValue();
ImageMapFactory.createMap(mapView, allImages);
});
}
else {
currentAlbumId = App.ex.getChosenAlbumId();
Optional<Album> optionalAlbum = albumService.getAlbumById(currentAlbumId);
if (optionalAlbum.isPresent()) {
Album album = optionalAlbum.get();
albumImages = album.getImages();
}
ImageMapFactory.createMap(mapView, albumImages);
}
/**
* Decide which images to fetch. Fetch album images if user is trying to view album, else all images.
*
* @return a task which fetches images
*/
private Task<List<Image>> getImageListTask() {
if (App.ex.getChosenAlbumId() == null)
return fetchAllImages;
return fetchAlbumImages;
}
/**
* Background task for fetching all images.
*/
private Task<List<Image>> fetchImages = new Task<>() {
private Task<List<Image>> fetchAllImages = new Task<>() {
@Override
protected List<Image> call() {
try {
......@@ -120,7 +149,103 @@ public class Map implements Initializable, MapComponentInitializedListener {
};
/**
* Change scene to Main page
* Background task for fetching all images in the current chosen album
* in {@link DataExchange}.
*/
private Task<List<Image>> fetchAlbumImages = new Task<>() {
@Override
protected List<Image> call() {
try {
Optional<Album> optionalAlbum = albumService.getAlbumById(App.ex.getChosenAlbumId());
if (optionalAlbum.isPresent())
return optionalAlbum.get().getImages();
} catch (Exception e) {
logger.error("[x] Failed to fetch images", e);
}
return new ArrayList<>();
}
};
/**
* Query for autocomplete predictions and bind them to the corresponding text field.
*
* @param keyEvent
*/
public void queryAutocomplete(KeyEvent keyEvent) {
QueryAutocompleteRequest queryAutocompleteRequest = PlacesApi.queryAutocomplete(geoApiContext, address.get());
queryAutocompleteRequest.setCallback(new PendingResult.Callback<>() {
@Override
public void onResult(AutocompletePrediction[] autocompletePredictions) {
autoCompletions = Stream.of(autocompletePredictions)
.map(prediction -> prediction.description)
.collect(Collectors.toList());
TextFields.bindAutoCompletion(addressTextField, autoCompletions);
}
@Override
public void onFailure(Throwable e) {
logger.error("[x] Failed to fetch predictions. Query='{}'", address.get(), e);
}
});
}
/**
* Search a Geocoding address with the current input in the address search text field.
* Centers the map on the first result.
*
* @param event
*/
public void searchGeocodingAddress(ActionEvent event) {
GeocodingApiRequest geoCodingAddressRequest = GeocodingApi.newRequest(geoApiContext).address(address.get());
geoCodingAddressRequest.setCallback(new PendingResult.Callback<>() {
@Override
public void onResult(GeocodingResult[] result) {
LatLng firstLatLngFound = result[0].geometry.location;
logger.info("[x] Geocoding result found: {}", result[0].formattedAddress);
Platform.runLater(() -> centerMapOnLocation(firstLatLngFound));
}
@Override
public void onFailure(Throwable e) {
logger.error("[x] Failed to fetch Geocoding locations", e);
if (e instanceof ArrayIndexOutOfBoundsException)
Platform.runLater(() -> showInfoAlert("No locations found"));
else
Platform.runLater(() -> showInfoAlert("Oops, an error occurred while searching."));
}
});
}
/**
* Center map on given {@link LatLng} and set appropriate zoom level.
*
* @param latLng the location to center on
*/
private void centerMapOnLocation(LatLng latLng) {
LatLong newLatLong = new LatLong(latLng.lat, latLng.lng);
googleMap.setCenter(newLatLong);
googleMap.setZoom(10);
}
/**
* Show an info alert to the user with given message.
*
* @param message the message containing info to the user
*/
private void showInfoAlert(String message) {
Alert alert = new Alert(Alert.AlertType.INFORMATION, message);
alert.show();
}
/**
* Change scene to Main page.
*
* @param mouseEvent
* @throws IOException
*/
......@@ -131,7 +256,8 @@ public class Map implements Initializable, MapComponentInitializedListener {
/**
* Change scene to Search page. It reads the value of the search
* field and if not empty it is passed to dataexchange
* field and if not empty it is passed to dataexchange.
*
* @param actionEvent
* @throws IOException
*/
......@@ -145,6 +271,7 @@ public class Map implements Initializable, MapComponentInitializedListener {
/**
* Change scene to Explore page
*
* @param actionEvent
* @throws IOException
*/
......@@ -185,13 +312,5 @@ public class Map implements Initializable, MapComponentInitializedListener {
App.setRoot("upload");
}
/**
* Search for images on a specific place.
*
* @param actionEvent
*/
public void MapSearch(ActionEvent actionEvent) {
//TODO: Make method
}
}
package NTNU.IDATT1002.database;
import NTNU.IDATT1002.Config;
import NTNU.IDATT1002.models.Image;
import NTNU.IDATT1002.models.Tag;
import NTNU.IDATT1002.models.User;
......@@ -24,7 +25,7 @@ public class LoadDatabase {
private static ImageService imageService;
static {
EntityManager entityManager = EntityManagerConfig.getEntityManager();
EntityManager entityManager = Config.getEntityManager();
userService = new UserService(entityManager);
albumService = new AlbumService(entityManager);
......
......@@ -58,10 +58,6 @@ public class GeoLocation {
return longitude;
}
public void setGeoLocationId(Long geoLocationId) {
this.geoLocationId = geoLocationId;
}
public void setLatitude(String altitude) {
this.latitude = altitude;
}
......@@ -74,6 +70,15 @@ public class GeoLocation {
this.metadata = metadata;
}
/**
* Verify that this geolocation has latitude and longitude different from 0.
*
* @return whether latitude and longitude are different from 0.
*/
public boolean hasLatLong() {
return Double.parseDouble(latitude) != 0 && Double.parseDouble(longitude) != 0;
}
@Override
public String toString() {
return "GeoLocation{" +
......
......@@ -7,10 +7,10 @@ import NTNU.IDATT1002.repository.LoginRepository;
import NTNU.IDATT1002.repository.UserRepository;
import NTNU.IDATT1002.utils.Authentication;
import java.util.List;
import javax.persistence.EntityManager;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
/**
......
......@@ -17,11 +17,6 @@
<class>NTNU.IDATT1002.models.GeoLocation</class>
<properties>
<!-- Configuring JDBC properties -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://mysql.stud.iie.ntnu.no:3306/g_sysutv_14" />
<property name="javax.persistence.jdbc.user" value="g_sysutv_14" />
<property name="javax.persistence.jdbc.password" value="tNdTRrwM" />
<!-- Hibernate properties -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
......
......@@ -61,12 +61,12 @@
<Font name="System Bold" size="24.0" />
</font>
</Text>
<TextField fx:id="search" prefHeight="44.0" prefWidth="664.0" promptText="Search for your location">
<TextField fx:id="addressTextField" onKeyPressed="#queryAutocomplete" onAction="#searchGeocodingAddress" prefHeight="44.0" prefWidth="664.0" promptText="Search for your location">
<font>
<Font size="18.0" />
</font>
</TextField>
<Button fx:id="searchBtn" mnemonicParsing="false" onAction="#MapSearch" prefHeight="44.0" prefWidth="99.0" text="SEARCH">
<Button fx:id="searchBtn" mnemonicParsing="false" onAction="#searchGeocodingAddress" prefHeight="44.0" prefWidth="99.0" text="GO">
<font>
<Font size="18.0" />
</font>
......
# Set this value to true if you want to configure another connection
OVERRIDE_DEFAULT_DB_CONFIG=false
# These values correspond to database hosted on https://www.digitalocean.com/
DB_DRIVER=com.mysql.cj.jdbc.Driver
DB_URL=jdbc:mysql://db-mysql-lon1-07155-do-user-7212587-0.a.db.ondigitalocean.com:25060/image_application
DB_USER=doadmin
DB_PASSWORD=s4oxtqu20e8r7tx5
DB_DIALECT=org.hibernate.dialect.MySQL8Dialect
DB_HBM2DDL=update
\ No newline at end of file
......@@ -14,7 +14,6 @@
<class>NTNU.IDATT1002.models.Album</class>
<class>NTNU.IDATT1002.models.Metadata</class>
<class>NTNU.IDATT1002.models.Tag</class>
<class>NTNU.IDATT1002.models.Histogram</class>
<class>NTNU.IDATT1002.models.GeoLocation</class>
<properties>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment