diff --git a/Accessibility-report.md b/Accessibility-report.md
new file mode 100644
index 000000000..23836d301
--- /dev/null
+++ b/Accessibility-report.md
@@ -0,0 +1,39 @@
+# Accessibility Report
+
+
+## Principles of Universal Design
+Our project hopes to follow as many principles of Universal design as possible. A comprehensive analysis of each principle is located below.
+
+### Principle 1: Equitable Use
+The Software displays a Map of the city chosen and nearby cities to be more accesible to those that prefer images over text.
+The UI also has the information in a chart form, with words spaced out to be more accessible to those that have trouble processing large chunks of information.
+### Principle 2: Flexibility in Use
+The software aims to be as perceptible as possible by allocating a conversion button for those that may not understand metric units.
+### Principle 3: Simple and Intuitive Use
+The UI is very simple as all the user has to do is type in a city in a search bar or press clearly labelled buttons, this is helpful for those that may not have much experience with our software.
+### Principle 4: Perceptible Information
+As mentioned before, the information uses varying modes of displaying information. Specifically, it is displayed in a chart on the right and a map is provided on the left for ease of
+use.
+### Principle 5: Tolerance for Error
+Nothing done on the software is permanant as all changes can be reversed. Cities can be retyped if spelt wrong, units can be converted infinitly and all other features change automatically to support the user.
+### Principle 6: Low Physical Effort
+The concept of physical effort is not much applicible to the software is online. However, the software only requires the user to type in the city name.
+All other calculations are done in the backend or done using simple buttons. The use of buttons requires very little effort.
+### Principle 7: Size and Space for Approach and Use
+This concept is not much applicable to our software design. However, our program is coded in java and can be run on any platform that contains the JVM. This is especially useful for
+users with disabilities that require unique hardware. For example, since the code can run on laptops, the user can plug in an accesible keyboard and still use our software.
+
+
+## The Market
+Realistically the most likely demographic for this project is those that are creating vacation plans, i.e travellers. Cultivating a list of cities nearby and using the software to
+compare the weather of those cities
+may be a feature most relevant to those planning what cities to go to. For example, if you were vacationing in Mykonos and it suddenly started raining before you went to the beach, you
+may use the application to find cities around you that may not be raining.
+The Weather Alerts feature is another one that may help travel planners plan for the weather as avoidng places with severe weather hazards is preferable.
+
+## Demographic Usage
+Since our UI only comes in smoall text on a default white background, the software is less likely to be used by those with visual impairments. Specifacally, people that are far sighted
+or those that struggle to concentrate in low contrast settings. A possible feature to add can be the use of another API to read out information and to implement a high contrast
+background. Asides from that, our software is easy to use due to the use of buttons and simple instructions and the conversion buttons. Important information such as alerts are clearly
+presented in a bold manner and unit conversion is implemented to allow users from all over the world to use our software. I dont see why any other demographics would be less likely to
+use this programs less.
diff --git a/README.md b/README.md
index 5e1d8b77c..c76f09b95 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,75 @@
-# Note Application
+# WeatherWizard
+Authors and Contributors: Annie B.,Sophie L., Linhao L., Harmanpreet S. and Ishayu S.
-This is a minimal example demonstrating usage of the
-password-protected user part of the API used in lab 5.
+WeatherWizard aims to provide users with a friendly accessible application to access Weather around the World. It can be used to plan trips, figure out historical weather data for projects and more. Many users have to use several tabs to compare weather in different destinations and this application aims to help users plan travel.
-You can find more information about the API endpoints in
-[the documentation](https://www.postman.com/cloudy-astronaut-813156/csc207-grade-apis-demo/documentation/fg3zkjm/5-password-protected-user).
+## Table of Contents
+1. [Features of Software](#features-of-software)
+2. [API](#api)
+3. [Installation](#installation)
+ - [Common Installation Issues](#common-installation-issues)
+4. [Usage Guide](#usage-guide)
+5. [License and Copyright Notice](#license-and-copyright-notice)
+6. [Contributions](#contributions)
+7. [Feedback](#feedback)
+8. [Accessibility](#accessibility)
+9. [Want to Know More?](#want-to-know-more)
-If your team is considering an application for which it would be convenient to
-store data in something like a database, you may find that the API calls demonstrated
-here will be useful in your project, as this will allow you to store
-an arbitrary JSON object associated with a username and password.
+## Features of Software.
+As mentioned previously the broad overarching sense of purpose lays in the ease in checking weather. The Software provides general information about the weather, humidity, windspeed, Visibility and sky condition. Asides from that, the application has the following features.
+* Accessible Map: The Software uses the JXMAP library to display a map that shows the location of the city.
+* Unit Conversion: The Software supports both Metric and Imperial Unit Conversion.
+* Weather Alerts: The Software displays Emergency Alerts regarding the weather including but not limited to Flooding, Tornado, Snow Storms and etc.
+* Nearby Cities: The Software displays a list of cities close to the main destination.
+* Comparisons: The Software allows for users to select up to two cities at a time to compare weather.
+* Search Historic Weather: The Software allows for the User to search for historic weather.
-In this application, a single note has a name (the "username" in terms of the API) and the note
-can be read by anyone who knows the name — but only edited by someone who
-knows the password for it.
+## API.
+The OpenWeather 3.0 API is called by city name to recieve weather information regarding temperature, humidity, windspeed & etc.
+ https://openweathermap.org/api/one-call-3
+
+The JXMAP library is used to produce a map to aid in the accesability of the software.
+msteiger/jxmapviewer2: JXMapViewer2
-You can see the documentation in the various files for more information.
+## Installation.
+The steps for Installation are as follows.
+1. Download the Java Development Kit (JDK) 17+, Apache Maven 3.8+ and an IDE of your choice (the instructions use Intellij).
+2. Clone the repository by Opening IntelliJ and selecting File, then New, then Project from Version Control. Choose Git and in this drop box paste the repository link (press the green code button on Github to copy the repository link). Choose a directory of your choice and click clone.
+3. Configure the project by going to File > Project Structure > Project Settings > Project. Here you should see what SDKs are setup. If you do not see the SDKs mentioned in step one, add them.
+4. Go to https://openweathermap.org/api/one-call-3 and obtain your own API key and replace with the one provided. Make sure to wait at least 7 hours as the API key activates several hours after creation.
-## Testing
+### Common Installation Issues
+* "Maven dependencies not resolved"
+ Go to the Maven Tool window and press Reload All Maven Project.
+* "Java Version Mismatch"
+ Go to File > Project Structure > Project and download Java 17.
+* "The Map is not Visible"
+ Ensure that JXMapViewer2 library is downloaded in the pom.xml file, if not add it.
-The repo also includes an example of a use case interactor test, as well as
-an example of an end-to-end test which automates button clicks and inspects
-the contents of the actual views. This is something we discussed in the lectures
-about testing in CA but had not provided a code example of before. Note, one
-could also inspect the contents of the ViewModel objects instead when testing
-CA to make a similar test which would be less dependent on the details of the
-specific UI implementation.
+## Usage Guide
+To use the software, navigate to the MainApplication file in Intellij located under the App folder. ( Src > Main > Java > App > MainApplication) and press the run file button.
-## Project Starter Code
+## License and Copyright notice.
+This project is protected by Canadian copyright law. Under the Copyright Act (RSC 1985, c. C-42), the content of this repository, including but not limited to source code, documentation, and other original works, is automatically protected by copyright as soon as it is created and fixed in a tangible form. All rights to the work are reserved to the original author(s) unless explicitly stated otherwise.
-Your team may choose to use this repo as starter code for your project. You could
-also use the lab 5 code — or start from an empty repo if your team prefers.
+## Contributions
+Contributions to this code are welcomed. If you would like to contribute, please ensure that the original authors are properly credited, you are welcome to create a pull request, that will be reviewed by the authors of this work.
-If you choose to use one of the repositories we have provided, you can either make
-a fork of it or copy the subset of code you want into a completely new repository.
+To make a fork on Github, press the fork button located across from the Project Name.
+
+
+
+
+## Feedback
+Any and all feedback on this project is much appreciated. To provide your feedback, please visit the following typeform.
+
+[Feedback Form](https://3g4dr8ponwb.typeform.com/to/XWR9pbZm)
+
+If an email is provided, our team will contact you within 24 hours.
+
+## Accessibility
+The project aims to be accessible to everyone as much as possible and thus has several features to ensure such. For more information, please check out our accessibility report by navigating to the accessibility-report.md file.
+
+## Want to know more?
+If you would like to know more please check out our presentation.
+[Link to the Presentation](https://docs.google.com/presentation/d/1UF4Ibt3WqdovrlT1XoJV6r2-A5ybwz8jJzVqMy94D5k/edit?usp=sharing)
diff --git a/pom.xml b/pom.xml
index 527f61e36..153ce4a3d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,29 @@
test
+
+ org.jxmapviewer
+ jxmapviewer2
+ 2.8
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.8.1
+ test
+
+
+
diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java
new file mode 100644
index 000000000..d75d229db
--- /dev/null
+++ b/src/main/java/app/AppBuilder.java
@@ -0,0 +1,240 @@
+package app;
+
+import javax.swing.*;
+
+import data_access.HistoricalWeatherDataAccessObject;
+import data_access.NearbyCitiesAccessObject;
+import data_access.WeatherDataAccessObject;
+import entity.Weather;
+import interface_adapter.CompareCities.CompareCitiesController;
+import interface_adapter.CompareCities.CompareCitiesPresenter;
+import interface_adapter.CompareCities.CompareCitiesViewModel;
+import interface_adapter.SearchResult.SearchResultController;
+import interface_adapter.SearchResult.SearchResultPresenter;
+import interface_adapter.SearchResult.SearchResultViewModel;
+import interface_adapter.alert_pop.AlertPopController;
+import interface_adapter.alert_pop.AlertPopPresenter;
+import interface_adapter.converter.ConverterController;
+import interface_adapter.converter.ConverterPresenter;
+import interface_adapter.nearby_list.NearbyListController;
+import interface_adapter.nearby_list.NearbyListPresenter;
+import interface_adapter.nearby_list.NearbyListViewModel;
+import interface_adapter.weather.WeatherController;
+import interface_adapter.weather.WeatherPresenter;
+import interface_adapter.weather.WeatherState;
+import interface_adapter.weather.WeatherViewModel;
+import use_case.note.CompareCities.CompareCitiesDataAccessInterface;
+import use_case.note.CompareCities.CompareCitiesInputBoundary;
+import use_case.note.CompareCities.CompareCitiesInteractor;
+import use_case.note.CompareCities.CompareCitiesOutputBoundary;
+import use_case.note.HistoricalWeatherDataAccessInterface;
+import use_case.note.search_result.SearchResultInteractor;
+import use_case.note.search_return.SearchReturnInputBoundary;
+import use_case.note.search_return.SearchReturnInteractor;
+import use_case.note.WeatherDataAccessInterface;
+import use_case.note.alert_pop.AlertPopInteractor;
+import use_case.note.alert_pop.AlertPopOutputBoundary;
+import use_case.note.convert_farenheit.ConvertFarenheitOutputBoundary;
+import use_case.note.convert_farenheit.ConvertInteractor;
+import use_case.note.nearby_list.NearbyCitiesAccessInterface;
+import use_case.note.nearby_list.NearbyListInteractor;
+import use_case.note.nearby_list.NearbyListOutputBoundary;
+import use_case.note.search_result.SearchResultInputBoundary;
+import use_case.note.search_result.SearchResultOutputBoundary;
+import use_case.note.search_return.SearchReturnOutputBoundary;
+import view.MainView;
+
+import java.beans.PropertyChangeEvent;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Builder for the Note Application.
+ */
+public class AppBuilder {
+
+ public static final int HEIGHT = 750;
+ public static final int WIDTH = 1500;
+ private static final String ERROR = "Error";
+ private WeatherDataAccessInterface weatherDAO;
+ private HistoricalWeatherDataAccessInterface historyDAO = new HistoricalWeatherDataAccessObject();
+ private WeatherViewModel weatherViewModel = new WeatherViewModel();
+ private SearchResultViewModel searchResultViewModel = new SearchResultViewModel();
+ private CompareCitiesViewModel compareCitiesViewModel = new CompareCitiesViewModel();
+ private NearbyListViewModel nearbyListViewModel = new NearbyListViewModel();
+ private MainView mainView = new MainView(nearbyListViewModel, weatherViewModel, searchResultViewModel,
+ new PropertyChangeEvent(weatherViewModel, "Weather", null, new WeatherState()));
+ private PropertyChangeEvent evt;
+
+ private SearchResultInputBoundary searchResultInputBoundary;
+ private CompareCitiesInputBoundary compareCitiesInputBoundary;
+ private SearchReturnInputBoundary searchReturnInputBoundary;
+
+ /**
+ * Sets the DAO to be used in this application.
+ * @param weatherDataAccess the DAO to use
+ * @return this builder
+ */
+ public AppBuilder addDAO(WeatherDataAccessInterface weatherDataAccess) {
+ weatherDAO = weatherDataAccess;
+ return this;
+ }
+
+ /**
+ * USE case.
+ * @return sjsjnk.
+ * @throws RuntimeException becais.
+ **/
+ public JFrame build() {
+ final JFrame frame = mainView;
+ frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ frame.setTitle("Weather Wizard");
+ frame.setSize(WIDTH, HEIGHT);
+
+ return frame;
+
+ }
+
+ /**
+ * USE case.
+ * @return sjsjnk.
+ * @throws RuntimeException becais.
+ **/
+
+ public AppBuilder addAlertPopUseCase() {
+ final AlertPopOutputBoundary outputBoundary = new AlertPopPresenter(weatherViewModel);
+ final WeatherDataAccessInterface accessInterface = new WeatherDataAccessInterface() {
+ @Override
+ public Weather getWeather(String city) throws IOException {
+ return weatherDAO.getWeather(city);
+ }
+
+ };
+
+ final AlertPopInteractor interactor = new AlertPopInteractor(accessInterface, outputBoundary);
+
+ final AlertPopController controller = new AlertPopController(interactor);
+ if (mainView == null) {
+ throw new RuntimeException(ERROR);
+ }
+ mainView.mapPanelView.setAlertPopController(controller);
+ return this;
+ }
+
+ /**
+ * USE case.
+ * @return sjsjnk.
+ * @throws RuntimeException becais.
+ **/
+
+ public AppBuilder addCompareCitiesUseCase() {
+ mainView.mapPanelView.setCompareCitiesViewModel(compareCitiesViewModel);
+ // outputBoundary refers to the presenter.
+ final CompareCitiesOutputBoundary outputBoundary = new CompareCitiesPresenter(compareCitiesViewModel);
+ final CompareCitiesDataAccessInterface dai = new WeatherDataAccessObject();
+
+ final CompareCitiesInteractor interactor = new CompareCitiesInteractor(dai, outputBoundary);
+
+ final CompareCitiesController controller = new CompareCitiesController(interactor);
+ mainView.mapPanelView.setCompareCitiesController(controller);
+ if (mainView == null) {
+ throw new RuntimeException("Error");
+ }
+ mainView.mapPanelView.setCompareCitiesController(controller);
+ return this;
+ }
+ /**
+ * USE case.
+ * @return sjsjnk.
+ * @throws RuntimeException becais.
+ **/
+
+ public AppBuilder addConvertUseCase() {
+ final ConvertFarenheitOutputBoundary outputBoundary = new ConverterPresenter(weatherViewModel);
+ final ConvertInteractor interactor = new ConvertInteractor(outputBoundary);
+
+ final ConverterController controller = new ConverterController(interactor);
+ if (mainView == null) {
+ throw new RuntimeException("Error");
+ }
+
+ mainView.weatherPanelView.setConverterController(controller);
+ return this;
+ }
+ /**
+ * USE case.
+ * @return sjsjnk.
+ * @throws RuntimeException becais.
+ **/
+
+ public AppBuilder addNearbyListUseCase() {
+ final NearbyListOutputBoundary outputBoundary = new NearbyListPresenter(nearbyListViewModel);
+ final NearbyCitiesAccessInterface dai = new NearbyCitiesAccessObject();
+
+ final NearbyListInteractor interactor = new NearbyListInteractor(outputBoundary, dai);
+
+ final NearbyListController controller = new NearbyListController(interactor);
+ if (mainView == null) {
+ throw new RuntimeException("Error");
+ }
+
+ mainView.mapPanelView.setNearbyListController(controller);
+ mainView.nearbyCitiesView.setNearbyListController(controller);
+ return this;
+ }
+ /**
+ * USE case.
+ * @return sjsjnk.
+ * @throws RuntimeException becais.
+ **/
+
+ public AppBuilder addSearchReturnUseCase() {
+ final SearchReturnOutputBoundary outputBoundary = new WeatherPresenter(weatherViewModel);
+ final SearchReturnInteractor interactor = new SearchReturnInteractor(outputBoundary, weatherDAO, historyDAO);
+
+ final WeatherController controller = new WeatherController(interactor);
+ mainView.mapPanelView.setWeatherController(controller);
+ if (mainView == null) {
+ throw new RuntimeException("Error");
+ }
+ mainView.mapPanelView.setWeatherController(controller);
+ mainView.nearbyCitiesView.setWeatherController(controller);
+ return this;
+ }
+ /**
+ * USE case.
+ * @return sjsjnk.
+ * @throws RuntimeException becais.
+ **/
+
+ public AppBuilder addSearchResultUseCase() {
+ final SearchResultOutputBoundary outputBoundary = new SearchResultPresenter(searchResultViewModel);
+ final SearchResultInteractor interactor = new SearchResultInteractor(outputBoundary, weatherDAO, historyDAO);
+
+ final SearchResultController controller = new SearchResultController(interactor);
+ mainView.mapPanelView.setSearchResultController(controller);
+ if (mainView == null) {
+ throw new RuntimeException("Error");
+ }
+ mainView.mapPanelView.setSearchResultController(controller);
+ return this;
+ }
+ /**
+ * USE case.
+ * @return sjsjnk.
+ * @throws RuntimeException becais.
+ **/
+
+ // add stuff for all the views
+ public AppBuilder addMainView() {
+ weatherViewModel = new WeatherViewModel();
+ searchResultViewModel = new SearchResultViewModel();
+ evt = new PropertyChangeEvent(weatherViewModel, "Weather", null, new WeatherState());
+ mainView = new MainView(nearbyListViewModel, weatherViewModel, searchResultViewModel, evt);
+ // mainView.mapPanelView.setSearchResultController(new SearchResultController(searchResultInputBoundary));
+ // mainView.mapPanealView.setWeatherController(new WeatherController(searchReturnInputBoundary));
+ // mainView.mapPanelView.setCompareCitiesController(new CompareCitiesController(compareCitiesInputBoundary));
+ return this;
+ }
+
+}
diff --git a/src/main/java/app/MainApplication.java b/src/main/java/app/MainApplication.java
new file mode 100644
index 000000000..f264da5e0
--- /dev/null
+++ b/src/main/java/app/MainApplication.java
@@ -0,0 +1,42 @@
+package app;
+
+import data_access.WeatherDataAccessObject;
+import use_case.note.WeatherDataAccessInterface;
+
+/**
+ * An application where we can view and add to a note stored by a user.
+ *
+ * This is a minimal example of using the password-protected user API from lab 5,
+ * but demonstrating the endpoint allowing you to store an arbitrary JSON object.
+ * This functionality could be used in any project where your team wants to persist
+ * data which is then accessible across devices.
+ * The code is intentionally somewhat incomplete to leave work to be done if your
+ * team were to choose to work on a project which would require similar functionality.
+ * For example, we have intentionally not created a full "Note" entity here, but
+ * rather just represented a note as a string.
+ *
+ * The ViewManager code has also been removed, since this minimal program only requires a single
+ * view. Your team may wish to bring back the ViewManager or make your own implementation of supporting
+ * switching between views depending on your project.
+ */
+public class MainApplication {
+
+ /**
+ * The main entry point of the application.
+ * @param args commandline arguments are ignored.
+ */
+ public static void main(String[] args) {
+
+ // create the data access and inject it into our builder
+ final WeatherDataAccessInterface noteDataAccess = new WeatherDataAccessObject();
+
+ final AppBuilder builder = new AppBuilder();
+ builder.addDAO(noteDataAccess)
+ .addSearchReturnUseCase()
+ .addSearchResultUseCase()
+ .addCompareCitiesUseCase()
+ .addConvertUseCase()
+ .addNearbyListUseCase()
+ .addAlertPopUseCase().build().setVisible(true);
+ }
+}
diff --git a/src/main/java/app/MainNoteApplication.java b/src/main/java/app/MainNoteApplication.java
deleted file mode 100644
index c37860156..000000000
--- a/src/main/java/app/MainNoteApplication.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package app;
-
-import data_access.DBNoteDataAccessObject;
-import use_case.note.NoteDataAccessInterface;
-
-/**
- * An application where we can view and add to a note stored by a user.
- *
- * This is a minimal example of using the password-protected user API from lab 5,
- * but demonstrating the endpoint allowing you to store an arbitrary JSON object.
- * This functionality could be used in any project where your team wants to persist
- * data which is then accessible across devices.
- * The code is intentionally somewhat incomplete to leave work to be done if your
- * team were to choose to work on a project which would require similar functionality.
- * For example, we have intentionally not created a full "Note" entity here, but
- * rather just represented a note as a string.
- *
- * The ViewManager code has also been removed, since this minimal program only requires a single
- * view. Your team may wish to bring back the ViewManager or make your own implementation of supporting
- * switching between views depending on your project.
- */
-public class MainNoteApplication {
-
- /**
- * The main entry point of the application.
- *
- * The program will show you the note currently saved in the system.
- * You are able to edit it and then save it to the system. You can refresh
- * to update the note to reflect what was saved most recently. This
- * uses the API from lab, so there is one database storing the note,
- * which means that if anyone updates the note, that is what you will
- * see when you refresh.
- *
- * You can generalize the code to allow you to
- * specify which "user" to save the note for, which will allow your team
- * to store information specific to your team which is password-protected.
- * The username and password used in this application are currently for
- * user jonathan_calver2, but you can change that. As you did in lab 3,
- * you will likely want to store password information locally rather than
- * in your repo. Or you can require the user to enter their credentials
- * in your application; it just depends on what your program's main
- * functionality.
- *
- * @param args commandline arguments are ignored
- */
- public static void main(String[] args) {
-
- // create the data access and inject it into our builder!
- final NoteDataAccessInterface noteDataAccess = new DBNoteDataAccessObject();
-
- final NoteAppBuilder builder = new NoteAppBuilder();
- builder.addNoteDAO(noteDataAccess)
- .addNoteView()
- .addNoteUseCase().build().setVisible(true);
- }
-}
diff --git a/src/main/java/app/NoteAppBuilder.java b/src/main/java/app/NoteAppBuilder.java
deleted file mode 100644
index a68cb9ad6..000000000
--- a/src/main/java/app/NoteAppBuilder.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package app;
-
-import javax.swing.JFrame;
-import javax.swing.WindowConstants;
-
-import interface_adapter.note.NoteController;
-import interface_adapter.note.NotePresenter;
-import interface_adapter.note.NoteViewModel;
-import use_case.note.NoteDataAccessInterface;
-import use_case.note.NoteInteractor;
-import use_case.note.NoteOutputBoundary;
-import view.NoteView;
-
-/**
- * Builder for the Note Application.
- */
-public class NoteAppBuilder {
- public static final int HEIGHT = 300;
- public static final int WIDTH = 400;
- private NoteDataAccessInterface noteDAO;
- private NoteViewModel noteViewModel = new NoteViewModel();
- private NoteView noteView;
- private NoteInteractor noteInteractor;
-
- /**
- * Sets the NoteDAO to be used in this application.
- * @param noteDataAccess the DAO to use
- * @return this builder
- */
- public NoteAppBuilder addNoteDAO(NoteDataAccessInterface noteDataAccess) {
- noteDAO = noteDataAccess;
- return this;
- }
-
- /**
- * Creates the objects for the Note Use Case and connects the NoteView to its
- * controller.
- * This method must be called after addNoteView!
- * @return this builder
- * @throws RuntimeException if this method is called before addNoteView
- */
- public NoteAppBuilder addNoteUseCase() {
- final NoteOutputBoundary noteOutputBoundary = new NotePresenter(noteViewModel);
- noteInteractor = new NoteInteractor(
- noteDAO, noteOutputBoundary);
-
- final NoteController controller = new NoteController(noteInteractor);
- if (noteView == null) {
- throw new RuntimeException("addNoteView must be called before addNoteUseCase");
- }
- noteView.setNoteController(controller);
- return this;
- }
-
- /**
- * Creates the NoteView and underlying NoteViewModel.
- * @return this builder
- */
- public NoteAppBuilder addNoteView() {
- noteViewModel = new NoteViewModel();
- noteView = new NoteView(noteViewModel);
- return this;
- }
-
- /**
- * Builds the application.
- * @return the JFrame for the application
- */
- public JFrame build() {
- final JFrame frame = new JFrame();
- frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
- frame.setTitle("Note Application");
- frame.setSize(WIDTH, HEIGHT);
-
- frame.add(noteView);
-
- // refresh so that the note will be visible when we start the program
- noteInteractor.executeRefresh();
-
- return frame;
-
- }
-}
diff --git a/src/main/java/data_access/DBNoteDataAccessObject.java b/src/main/java/data_access/DBNoteDataAccessObject.java
deleted file mode 100644
index dadb0cab0..000000000
--- a/src/main/java/data_access/DBNoteDataAccessObject.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package data_access;
-
-import java.io.IOException;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import entity.User;
-import okhttp3.MediaType;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
-import use_case.note.DataAccessException;
-import use_case.note.NoteDataAccessInterface;
-
-/**
- * The DAO for accessing notes stored in the database.
- * This class demonstrates how your group can use the password-protected user
- * endpoints of the API used in lab 5 to store persistent data in your program.
- *
- * You can also refer to the lab 5 code for signing up a new user and other use cases.
- *
- * See
- *
- * the documentation
- * of the API for more details.
- */
-public class DBNoteDataAccessObject implements NoteDataAccessInterface {
- private static final int SUCCESS_CODE = 200;
- private static final int CREDENTIAL_ERROR = 401;
- private static final String CONTENT_TYPE_LABEL = "Content-Type";
- private static final String CONTENT_TYPE_JSON = "application/json";
- private static final String STATUS_CODE_LABEL = "status_code";
- private static final String USERNAME = "username";
- private static final String PASSWORD = "password";
- private static final String MESSAGE = "message";
-
- @Override
- public String saveNote(User user, String note) throws DataAccessException {
- final OkHttpClient client = new OkHttpClient().newBuilder()
- .build();
-
- // POST METHOD
- final MediaType mediaType = MediaType.parse(CONTENT_TYPE_JSON);
- final JSONObject requestBody = new JSONObject();
- requestBody.put(USERNAME, user.getName());
- requestBody.put(PASSWORD, user.getPassword());
- final JSONObject extra = new JSONObject();
- extra.put("note", note);
- requestBody.put("info", extra);
- final RequestBody body = RequestBody.create(requestBody.toString(), mediaType);
- final Request request = new Request.Builder()
- .url("http://vm003.teach.cs.toronto.edu:20112/modifyUserInfo")
- .method("PUT", body)
- .addHeader(CONTENT_TYPE_LABEL, CONTENT_TYPE_JSON)
- .build();
- try {
- final Response response = client.newCall(request).execute();
-
- final JSONObject responseBody = new JSONObject(response.body().string());
-
- if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) {
- return loadNote(user);
- }
- else if (responseBody.getInt(STATUS_CODE_LABEL) == CREDENTIAL_ERROR) {
- throw new DataAccessException("message could not be found or password was incorrect");
- }
- else {
- throw new DataAccessException("database error: " + responseBody.getString(MESSAGE));
- }
- }
- catch (IOException | JSONException ex) {
- throw new DataAccessException(ex.getMessage());
- }
- }
-
- @Override
- public String loadNote(User user) throws DataAccessException {
- // Make an API call to get the user object.
- final String username = user.getName();
- final OkHttpClient client = new OkHttpClient().newBuilder().build();
- final Request request = new Request.Builder()
- .url(String.format("http://vm003.teach.cs.toronto.edu:20112/user?username=%s", username))
- .addHeader("Content-Type", CONTENT_TYPE_JSON)
- .build();
- try {
- final Response response = client.newCall(request).execute();
-
- final JSONObject responseBody = new JSONObject(response.body().string());
-
- if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) {
- final JSONObject userJSONObject = responseBody.getJSONObject("user");
- final JSONObject data = userJSONObject.getJSONObject("info");
- return data.getString("note");
- }
- else {
- throw new DataAccessException(responseBody.getString(MESSAGE));
- }
- }
- catch (IOException | JSONException ex) {
- throw new RuntimeException(ex);
- }
- }
-}
diff --git a/src/main/java/data_access/HistoricalWeatherDataAccessObject.java b/src/main/java/data_access/HistoricalWeatherDataAccessObject.java
new file mode 100644
index 000000000..1515229d5
--- /dev/null
+++ b/src/main/java/data_access/HistoricalWeatherDataAccessObject.java
@@ -0,0 +1,134 @@
+package data_access;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import entity.Weather;
+import use_case.note.HistoricalWeatherDataAccessInterface;
+
+/**
+ * This class provides the service of saving and getting historical weather data.
+ */
+public class HistoricalWeatherDataAccessObject implements HistoricalWeatherDataAccessInterface {
+ @Override
+ public void saveWeather(Weather weather, String timeStamp) throws IOException {
+
+ // Save the weather data to the database
+ // Convert the Weather object to JSON
+ final StringBuilder jsonBuilder = new StringBuilder();
+ final String format = "\",\n";
+ final String format4 = ",\n";
+ jsonBuilder
+ .append("{\n")
+ .append(" \"timeStamp\": \"").append(timeStamp).append(format)
+ .append(" \"description\": \"").append(weather.getDescription()).append(format)
+ .append(" \"city\": \"").append(weather.getCityName()).append(format)
+ .append(" \"longitude\": ").append(weather.getLon()).append(format4)
+ .append(" \"latitude\": ").append(weather.getLat()).append(format4)
+ .append(" \"temperature\": ").append(weather.getTemperature()).append(format4)
+ .append(" \"looks\": \"").append(weather.getWeather()).append(format)
+ .append(" \"alertDescription\": \"").append(weather.getAlertDescription()).append(format)
+ .append(" \"humidity\": ").append(weather.getHumidity()).append(format4)
+ .append(" \"windSpeed\": ").append(weather.getWindSpeed()).append(format4)
+ .append(" \"visibility\": ").append(weather.getVisibility()).append("\n")
+ .append("}");
+
+ // Convert StringBuilder to String
+ final String weatherJson = jsonBuilder.toString();
+
+ // Write JSON to a file
+
+ final String relativePath = "src/main/resources/weather.json";
+ final File file = new File(relativePath);
+
+ try {
+ final StringBuilder finalJson = new StringBuilder();
+ if (!file.exists() || file.length() == 0) {
+ // If file does not exist or is empty, create a new JSON array
+ finalJson.append("[\n").append(weatherJson).append("\n]");
+ }
+ else {
+ // Read existing data from the file
+ String existingJson = Files.readString(file.toPath(), StandardCharsets.UTF_8);
+
+ // Remove the closing bracket of the JSON array
+ existingJson = existingJson.trim();
+ if (existingJson.endsWith("]")) {
+ existingJson = existingJson.substring(0, existingJson.length() - 1);
+ }
+
+ // Append the new object with a comma if there's existing data
+ finalJson.append(existingJson).append(",\n").append(weatherJson).append("\n]");
+ }
+
+ // Write the updated JSON data to the file
+ try (FileWriter fileWriter = new FileWriter(file, false)) {
+ fileWriter.write(finalJson.toString());
+ System.out.println("Successfully appended weather data to weather.json");
+ }
+ }
+ catch (IOException excep) {
+ excep.printStackTrace();
+ System.out.println("Failed to append weather data to weather.json");
+ }
+ }
+
+ @Override
+ public Weather getWeather(String city, String timestamp) throws IOException {
+ // Get the weather data from the database
+ // Read the JSON from the file
+ final String filePath = "weather.json";
+ try {
+ final URL resource = getClass().getClassLoader().getResource(filePath);
+ if (resource == null) {
+ throw new FileNotFoundException("File not found: src/main/resources/weather.json");
+ }
+
+ // Read the content of the JSON file as a String
+ final String jsonString = Files.readString(Paths.get(resource.toURI()), StandardCharsets.UTF_8);
+ final JSONArray weatherArray = new JSONArray(jsonString);
+
+ for (int i = 0; i < weatherArray.length(); i++) {
+ // Getting the JSONObject at the index i
+ final JSONObject weatherObject = weatherArray.getJSONObject(i);
+ // Getting the city from the JSONObject
+ final String cityNameCall = weatherObject.getString("city");
+ // Getting the timestamp from the JSONObject
+ final String timeStamp = weatherObject.getString("timeStamp");
+ // Checking if the city and timestamp match the input
+ if (cityNameCall.equals(city) && timeStamp.equals(timestamp)) {
+ // Create weather object
+ final Weather weather = new Weather(
+ cityNameCall,
+ weatherObject.getInt("temperature"),
+ weatherObject.getString("looks"),
+ weatherObject.getString("description"),
+ weatherObject.getInt("windSpeed"),
+ weatherObject.getInt("humidity"),
+ weatherObject.getInt("visibility"),
+ weatherObject.getInt("longitude"),
+ weatherObject.getInt("latitude"),
+ weatherObject.getString("alertDescription")
+ );
+ return weather;
+ }
+
+ }
+ }
+ catch (IOException | URISyntaxException exe) {
+ exe.printStackTrace();
+ }
+ throw new IOException("Weather data not found");
+ }
+
+}
diff --git a/src/main/java/data_access/InMemoryUserDataAccessObject.java b/src/main/java/data_access/InMemoryUserDataAccessObject.java
new file mode 100644
index 000000000..94e4f183d
--- /dev/null
+++ b/src/main/java/data_access/InMemoryUserDataAccessObject.java
@@ -0,0 +1,55 @@
+package data_access;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import entity.Weather;
+import org.json.JSONException;
+import use_case.note.CompareCities.CompareCitiesDataAccessInterface;
+
+/**
+ * In-memory implementation of the DAO for storing weather data. This implementation does
+ * NOT persist data between runs of the program.
+ */
+public class InMemoryUserDataAccessObject implements CompareCitiesDataAccessInterface {
+ private final Map weathers = new HashMap<>();
+
+ @Override
+ public Weather getWeather(String identifier) throws IOException {
+ if ("Toronto".equalsIgnoreCase(identifier)) {
+ final Weather torontoweather = new Weather("Toronto", 10.5, "rain",
+ "first description", 0.0, 1, 2, 3.0, 4.0, "No alerts");
+ return torontoweather;
+ }
+ else if ("Tokyo".equalsIgnoreCase(identifier)) {
+ final Weather tokyoweather = new Weather("Tokyo", 1.0, "cloud", "second description",
+ 2.0, 2, 1000, 20.0, 25.0, "Alerts");
+ return tokyoweather;
+ }
+ throw new IOException("Failed to fetch weather data");
+ }
+
+ @Override
+ public boolean isCityExist(String cityname){
+ if (cityname.equals("Toronto")||cityname.equals("Tokyo")){
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void saveWeatherinfor(Weather weather) {
+ weathers.put(weather.getCityName(), weather);
+ }
+
+ @Override
+ public Map getcitytoweather() {
+ return weathers;
+ }
+
+ @Override
+ public void clearcitytoweather() {
+ weathers.clear();
+ }
+}
diff --git a/src/main/java/data_access/NearbyCitiesAccessObject.java b/src/main/java/data_access/NearbyCitiesAccessObject.java
new file mode 100644
index 000000000..698a03418
--- /dev/null
+++ b/src/main/java/data_access/NearbyCitiesAccessObject.java
@@ -0,0 +1,58 @@
+package data_access;
+
+import org.jetbrains.annotations.NotNull;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import use_case.note.nearby_list.NearbyCitiesAccessInterface;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the service of getting nearby cities.
+ */
+public class NearbyCitiesAccessObject implements NearbyCitiesAccessInterface {
+ private static final double LOWER_LAT = -90.0;
+ private static final double UPPER_LAT = 90.0;
+ private static final double LOWER_LON = -180.0;
+ private static final double UPPER_LON = 180.0;
+ private static final double COMPARE_DIFF = 10.0;
+ private static final double ERROR_DIFF = 0.015;
+
+ @Override
+ public List getNearbyCities(double latitude, double longitude) throws IOException {
+ if (latitude < LOWER_LAT || latitude > UPPER_LAT || longitude < LOWER_LON || longitude > UPPER_LON) {
+ throw new IOException();
+ }
+ try {
+ final String jsonString = Files
+ .readString(Paths.get(getClass().getClassLoader().getResource("cities_list.json").toURI()));
+ return getCityNames(latitude, longitude, jsonString);
+ }
+ catch (IOException | URISyntaxException ex) {
+ throw new IOException();
+ }
+ }
+
+ @NotNull
+ private static ArrayList getCityNames(double latitude, double longitude, String jsonString) {
+ final JSONArray jsonArray = new JSONArray(jsonString);
+
+ final ArrayList nearbyCities = new ArrayList<>();
+
+ for (int i = 0; i < jsonArray.length(); i++) {
+ final JSONObject city = jsonArray.getJSONObject(i);
+ final double latDiff = Math.abs(latitude - city.getDouble("lat"));
+ final double lonDiff = Math.abs(longitude - city.getDouble("lon"));
+ if (latDiff < COMPARE_DIFF && lonDiff < COMPARE_DIFF && latDiff > ERROR_DIFF && lonDiff > ERROR_DIFF) {
+ nearbyCities.add(city.getString("name"));
+ }
+ }
+ return nearbyCities;
+ }
+}
diff --git a/src/main/java/data_access/TestDataAccessObject.java b/src/main/java/data_access/TestDataAccessObject.java
new file mode 100644
index 000000000..e97b75c7f
--- /dev/null
+++ b/src/main/java/data_access/TestDataAccessObject.java
@@ -0,0 +1,26 @@
+package data_access;
+
+import entity.Weather;
+import use_case.note.WeatherDataAccessInterface;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class TestDataAccessObject implements WeatherDataAccessInterface {
+
+ private final Map citytoweather = new HashMap<>();
+
+ /**
+ * Stores a Weather object into the citytoweather map in the class.
+ * @param weather a Weather object containing the city name and all other weather information.
+ */
+ public void saveWeatherinfor(Weather weather) {
+ citytoweather.put(weather.getCityName(), weather);
+ }
+
+ @Override
+ public Weather getWeather(String citySearch) {
+ return citytoweather.get(citySearch);
+ }
+}
diff --git a/src/main/java/data_access/WeatherDataAccessObject.java b/src/main/java/data_access/WeatherDataAccessObject.java
new file mode 100644
index 000000000..91fa0c141
--- /dev/null
+++ b/src/main/java/data_access/WeatherDataAccessObject.java
@@ -0,0 +1,136 @@
+package data_access;
+
+import entity.Weather;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONArray;
+
+import use_case.note.CompareCities.CompareCitiesDataAccessInterface;
+import use_case.note.WeatherDataAccessInterface;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class runs the API and creates a weather DAO.
+ **/
+
+public class WeatherDataAccessObject implements WeatherDataAccessInterface, CompareCitiesDataAccessInterface {
+ private static final String API_KEY = "7cce48d7f1f6785f54c0d08aa117ad83";
+ private static final String MAIN = "main";
+ // private static String city;
+ private static final int SUCCESS_CODE = 200;
+ private static final String CONTENT_TYPE_JSON = "application/json";
+ private static final String STATUS_CODE_LABEL = "cod";
+ private static final String WEATHER_LIST = "list";
+ private static final String MESSAGE = "This city is not found";
+ private boolean cityexist;
+ private final Map citytoweather = new HashMap<>();
+
+ @Override
+ public boolean isCityExist(String citySearch) {
+ final OkHttpClient client = new OkHttpClient().newBuilder().build();
+
+ // Create a request to the OpenWeather API
+ final Request request = new Request.Builder()
+ .url(String.format("http://api.openweathermap.org/data/2.5/forecast?q=%s&appid=%s&units=metric", citySearch, API_KEY))
+ .addHeader("Content-Type", CONTENT_TYPE_JSON)
+ .build();
+
+ try {
+ final Response response = client.newCall(request).execute();
+
+ // Parse the response
+ final JSONObject responseBody = new JSONObject(response.body().string());
+
+ // Check if the status code is 200 (Success)
+ if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) {
+ // City exists, as the API returned successful data
+ return true;
+ }
+ else {
+ // City doesn't exist (API returned a different code or error message)
+ return false;
+ }
+ }
+ catch (IOException | JSONException ex) {
+ // If there was an error with the API call (e.g., network error, parsing error), assume city doesn't exist
+ return false;
+ }
+ }
+
+ @Override
+ public Weather getWeather(String citySearch) throws IOException {
+ // Make an API call to get the user object.
+ final OkHttpClient client = new OkHttpClient().newBuilder().build();
+
+ // creating file
+ final Request request = new Request.Builder()
+ .url(String.format("http://api.openweathermap.org/data/2.5/forecast?q=%s&appid="
+ + API_KEY + "&units=metric", citySearch))
+ .addHeader("Content-Type", CONTENT_TYPE_JSON)
+ .build();
+ try {
+ final Response response = client.newCall(request).execute();
+
+ final JSONObject responseBody = new JSONObject(response.body().string());
+
+ if (responseBody.getInt(STATUS_CODE_LABEL) == SUCCESS_CODE) {
+ final JSONObject weatherJSON = responseBody.getJSONArray(WEATHER_LIST).getJSONObject(0);
+ this.cityexist = true;
+ // get individual items from the json object
+ final JSONObject coordJSON = responseBody.getJSONObject("city").getJSONObject("coord");
+ final double lon = coordJSON.getDouble("lon");
+ final double lat = coordJSON.getDouble("lat");
+
+ final int temp = (int) weatherJSON.getJSONObject(MAIN).getDouble("temp");
+ final int humidity = (int) weatherJSON.getJSONObject(MAIN).getDouble("humidity");
+ final int windspeed = (int) weatherJSON.getJSONObject("wind").getDouble("speed");
+
+ // final String looks = weatherJSON.getJSONObject("weather").getString(MAIN);
+ final String looks = weatherJSON.getJSONArray("weather").getJSONObject(0).getString(MAIN);
+ final int visibility = weatherJSON.getInt("visibility");
+ // final String description = weatherJSON.getJSONObject("weather").getString("description");
+ final String description = weatherJSON.getJSONArray("weather").getJSONObject(0).getString("description");
+ String alertDescription = "no weather alert";
+ if (weatherJSON.has("alerts")) {
+ final JSONArray alertsArray = weatherJSON.getJSONArray("alerts");
+ if (alertsArray.length() > 0) {
+ alertDescription = alertsArray.getJSONObject(0).getString("description");
+ }
+ }
+
+ return new Weather(citySearch, temp, looks, description, windspeed, humidity,
+ visibility, lon, lat, alertDescription);
+ }
+ else {
+ throw new IOException(responseBody.getString(MESSAGE));
+ }
+ }
+ catch (IOException | JSONException ex) {
+ throw new IOException(ex);
+ }
+ }
+
+ @Override
+ public void saveWeatherinfor(Weather weather) {
+ citytoweather.put(weather.getCityName(), weather);
+ }
+
+ @Override
+ public Map getcitytoweather() {
+ return this.citytoweather;
+ }
+
+ /*
+ * This method "clean" the elements inside this.citytoweather we don't want to accumulate pairs.
+ */
+ @Override
+ public void clearcitytoweather() {
+ this.citytoweather.clear();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/entity/User.java b/src/main/java/entity/User.java
deleted file mode 100644
index e0c57e9a6..000000000
--- a/src/main/java/entity/User.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package entity;
-
-/**
- * The representation of a password-protected user for our program.
- */
-public class User {
-
- private final String name;
- private final String password;
-
- public User(String name, String password) {
- this.name = name;
- this.password = password;
- }
-
- public String getName() {
- return name;
- }
-
- public String getPassword() {
- return password;
- }
-
-}
diff --git a/src/main/java/entity/Weather.java b/src/main/java/entity/Weather.java
new file mode 100644
index 000000000..9e2ed920d
--- /dev/null
+++ b/src/main/java/entity/Weather.java
@@ -0,0 +1,131 @@
+package entity;
+
+/**
+ * The representation of a password-protected user for our program.
+ */
+public class Weather {
+
+ private static final int ABSOLUTEZERO = -500;
+ private static final int IMPOSSIBLESPEED = 800;
+ private double temperature;
+ private String weather;
+ private final String description;
+ private double windSpeed;
+ private final int humidity;
+ private final int visibility;
+ private boolean metric;
+ private final String cityName;
+ private final double lon;
+ private final double lat;
+ private final String alertDescription;
+ private int farenheit = ABSOLUTEZERO;
+ private int miles = IMPOSSIBLESPEED;
+
+ private final int celcius;
+ private final int kilometers;
+
+ public Weather(String city, double temperature, String weather, String description, double windSpeed,
+ int humidity, int visibility, double lon, double lat, String alertDescription) {
+ this.temperature = temperature;
+ this.windSpeed = windSpeed;
+ this.weather = weather;
+
+ // just for converter
+ this.celcius = (int) temperature;
+ this.kilometers = (int) windSpeed;
+
+ this.description = description;
+ this.humidity = humidity;
+ this.visibility = visibility;
+ this.cityName = city;
+ this.metric = true;
+ this.lon = lon;
+ this.lat = lat;
+ this.alertDescription = alertDescription;
+ }
+
+ public int getKilometers() {
+ return kilometers;
+ }
+
+ public int getMiles() {
+ return miles;
+ }
+
+ public void setMiles(int miles) {
+ this.miles = miles;
+ }
+
+ public int getCelcius() {
+ return celcius;
+ }
+
+ public int getfaren() {
+ return farenheit;
+ }
+
+ public void setfarenheit(int faren) {
+ this.farenheit = faren;
+ }
+
+ public void setWeather(String weather) {
+ this.weather = weather;
+ }
+
+ public String getCityName() {
+ return cityName;
+ }
+
+ public double getLat() {
+ return lat;
+ }
+
+ public double getLon() {
+ return lon;
+ }
+
+ public boolean isMetric() {
+ return metric;
+ }
+
+ public void setMetric(boolean metric) {
+ this.metric = metric;
+ }
+
+ public void setTemperature(float temperature) {
+ this.temperature = temperature;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getAlertDescription() {
+ return alertDescription;
+
+ }
+
+ public void setWindSpeed(float windSpeed) {
+ this.windSpeed = windSpeed;
+ }
+
+ public String getWeather() {
+ return weather;
+ }
+
+ public double getTemperature() {
+ return temperature;
+ }
+
+ public double getWindSpeed() {
+ return windSpeed;
+ }
+
+ public int getHumidity() {
+ return humidity;
+ }
+
+ public int getVisibility() {
+ return visibility;
+ }
+}
diff --git a/src/main/java/interface_adapter/CompareCities/CompareCitiesController.java b/src/main/java/interface_adapter/CompareCities/CompareCitiesController.java
new file mode 100644
index 000000000..9fde6bc1d
--- /dev/null
+++ b/src/main/java/interface_adapter/CompareCities/CompareCitiesController.java
@@ -0,0 +1,25 @@
+package interface_adapter.CompareCities;
+
+import use_case.note.CompareCities.CompareCitiesInputBoundary;
+import use_case.note.CompareCities.CompareCitiesInputData;
+/**
+ * The Controller for teh Compare Cities Use case.
+ */
+
+public class CompareCitiesController {
+ private final CompareCitiesInputBoundary interactor;
+
+ public CompareCitiesController(CompareCitiesInputBoundary interactor) {
+ this.interactor = interactor;
+ }
+
+ /**
+ * The execute method.
+ * @param firstcityName is the first city to compare.
+ * @param secondcityName is the second city.
+ */
+ public void execute(String firstcityName, String secondcityName) {
+ final CompareCitiesInputData compareCitiesInputData = new CompareCitiesInputData(firstcityName, secondcityName);
+ interactor.execute(compareCitiesInputData);
+ }
+}
diff --git a/src/main/java/interface_adapter/CompareCities/CompareCitiesPresenter.java b/src/main/java/interface_adapter/CompareCities/CompareCitiesPresenter.java
new file mode 100644
index 000000000..7db014fb9
--- /dev/null
+++ b/src/main/java/interface_adapter/CompareCities/CompareCitiesPresenter.java
@@ -0,0 +1,34 @@
+package interface_adapter.CompareCities;
+
+import use_case.note.CompareCities.CompareCitiesOutPutData;
+import use_case.note.CompareCities.CompareCitiesOutputBoundary;
+
+/**
+ * The Presenter for the Compare Cities Use case.
+ */
+public class CompareCitiesPresenter implements CompareCitiesOutputBoundary {
+ private final CompareCitiesViewModel viewModel;
+
+ public CompareCitiesPresenter(CompareCitiesViewModel viewModel) {
+ this.viewModel = viewModel;
+ }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+ // viewModel.getState will provide a CompareCitiesState
+ viewModel.getState().setError(errorMessage);
+ viewModel.firePropertyChanged();
+ }
+
+ @Override
+ public void prepareSuccessView(CompareCitiesOutPutData outputData) {
+ viewModel.getState().setError(null);
+ viewModel.getState().setFirstCityName(outputData.getFirstCityname());
+ viewModel.getState().setSecondCityName(outputData.getSecondCityname());
+ viewModel.getState().setFirstWeather(outputData.getFirstWeather());
+ viewModel.getState().setSecondWeather(outputData.getSecondWeather());
+
+ viewModel.firePropertyChanged("CompareCity");
+ }
+}
+
diff --git a/src/main/java/interface_adapter/CompareCities/CompareCitiesState.java b/src/main/java/interface_adapter/CompareCities/CompareCitiesState.java
new file mode 100644
index 000000000..d6758be40
--- /dev/null
+++ b/src/main/java/interface_adapter/CompareCities/CompareCitiesState.java
@@ -0,0 +1,65 @@
+package interface_adapter.CompareCities;
+
+import entity.Weather;
+/**
+ * The State for the Compare Cities Use case.
+ */
+
+public class CompareCitiesState {
+ private String error;
+ private String firstCityName;
+ private String secondCityName;
+ private Weather firstWeather;
+ private Weather secondWeather;
+
+ public CompareCitiesState(CompareCitiesState copy) {
+ this.error = copy.error;
+ this.firstCityName = copy.firstCityName;
+ this.secondCityName = copy.secondCityName;
+ this.setFirstWeather(copy.getFirstWeather());
+ this.setSecondWeather(copy.getSecondWeather());
+ }
+
+ public CompareCitiesState() {
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public String getFirstCityName() {
+ return firstCityName;
+ }
+
+ public String getSecondCityName() {
+ return secondCityName;
+ }
+
+ public void setError(String error) {
+ this.error = error;
+ }
+
+ public void setFirstCityName(String firstCityName) {
+ this.firstCityName = firstCityName;
+ }
+
+ public void setSecondCityName(String secondCityName) {
+ this.secondCityName = secondCityName;
+ }
+
+ public Weather getFirstWeather() {
+ return firstWeather;
+ }
+
+ public void setFirstWeather(Weather firstWeather) {
+ this.firstWeather = firstWeather;
+ }
+
+ public Weather getSecondWeather() {
+ return secondWeather;
+ }
+
+ public void setSecondWeather(Weather secondWeather) {
+ this.secondWeather = secondWeather;
+ }
+}
diff --git a/src/main/java/interface_adapter/CompareCities/CompareCitiesViewModel.java b/src/main/java/interface_adapter/CompareCities/CompareCitiesViewModel.java
new file mode 100644
index 000000000..e3a22a6f5
--- /dev/null
+++ b/src/main/java/interface_adapter/CompareCities/CompareCitiesViewModel.java
@@ -0,0 +1,13 @@
+package interface_adapter.CompareCities;
+
+import interface_adapter.ViewModel;
+/**
+ * The View Model for the Compare Cities Use case.
+ */
+
+public class CompareCitiesViewModel extends ViewModel {
+ public CompareCitiesViewModel() {
+ super("CompareCities");
+ setState(new CompareCitiesState());
+ }
+}
diff --git a/src/main/java/interface_adapter/SearchResult/SearchResultController.java b/src/main/java/interface_adapter/SearchResult/SearchResultController.java
new file mode 100644
index 000000000..24c78221a
--- /dev/null
+++ b/src/main/java/interface_adapter/SearchResult/SearchResultController.java
@@ -0,0 +1,31 @@
+package interface_adapter.SearchResult;
+
+import use_case.note.search_result.SearchResultInputBoundary;
+import use_case.note.search_result.SearchResultInputData;
+
+/**
+ * The controller for the search result view. It is responsible for executing the search result use case.
+ */
+public class SearchResultController {
+
+ private final SearchResultInputBoundary searchResultInputBoundary;
+
+ // Constructor that injects the use case's input boundary
+ public SearchResultController(SearchResultInputBoundary searchResultInputBoundary) {
+ this.searchResultInputBoundary = searchResultInputBoundary;
+ }
+
+ /**
+ * Executes the search result use case with the given city name and date.
+ *
+ * @param cityName the name of the city to search for
+ * @param date the date to search for
+ */
+ public void execute(String cityName, String date) {
+ // Create a SearchResultInputData object to encapsulate the input data
+ final SearchResultInputData inputData = new SearchResultInputData(cityName, date);
+ // Call the use case's execute method with the input data
+ searchResultInputBoundary.execute(inputData);
+ }
+}
+
diff --git a/src/main/java/interface_adapter/SearchResult/SearchResultPresenter.java b/src/main/java/interface_adapter/SearchResult/SearchResultPresenter.java
new file mode 100644
index 000000000..179775348
--- /dev/null
+++ b/src/main/java/interface_adapter/SearchResult/SearchResultPresenter.java
@@ -0,0 +1,29 @@
+package interface_adapter.SearchResult;
+
+import use_case.note.search_result.SearchResultOutputBoundary;
+import use_case.note.search_result.SearchResultOutputData;
+/**
+ * Presenter for the Search Result use case.
+ */
+
+public class SearchResultPresenter implements SearchResultOutputBoundary {
+
+ private final SearchResultViewModel viewModel;
+
+ public SearchResultPresenter(SearchResultViewModel viewModel) {
+ this.viewModel = viewModel;
+ }
+
+ @Override
+ public void presentSuccessView(SearchResultOutputData searchResultOutputData) {
+ final SearchResultState state = new SearchResultState();
+ state.setWeather(searchResultOutputData.getWeather());
+ viewModel.setState(state);
+ viewModel.firePropertyChanged("searchResult");
+ }
+
+ @Override
+ public void presentFailView(String errorMessage) {
+ viewModel.getState().setError(errorMessage);
+ }
+}
diff --git a/src/main/java/interface_adapter/SearchResult/SearchResultState.java b/src/main/java/interface_adapter/SearchResult/SearchResultState.java
new file mode 100644
index 000000000..c42cf7716
--- /dev/null
+++ b/src/main/java/interface_adapter/SearchResult/SearchResultState.java
@@ -0,0 +1,46 @@
+package interface_adapter.SearchResult;
+
+import entity.Weather;
+
+/**
+ * The State for a search result.
+ * For this example, a note is simplay a string.
+ */
+public class SearchResultState {
+ private Weather weather;
+ private String error;
+ private String date;
+ private String cityName;
+
+ public Weather getWeather() {
+ return weather;
+ }
+
+ public String getCityName() {
+ return cityName;
+ }
+
+ public void setCityName(String cityName) {
+ this.cityName = cityName;
+ }
+
+ public void setWeather(Weather weather) {
+ this.weather = weather;
+ }
+
+ public void setError(String errorMessage) {
+ this.error = errorMessage;
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public void setDate(String date) {
+ this.date = date;
+ }
+
+ public String getDate() {
+ return date;
+ }
+}
diff --git a/src/main/java/interface_adapter/SearchResult/SearchResultViewModel.java b/src/main/java/interface_adapter/SearchResult/SearchResultViewModel.java
new file mode 100644
index 000000000..d0094b8cb
--- /dev/null
+++ b/src/main/java/interface_adapter/SearchResult/SearchResultViewModel.java
@@ -0,0 +1,13 @@
+package interface_adapter.SearchResult;
+
+import interface_adapter.ViewModel;
+/**
+ * View Model for the Search Result use case.
+ */
+
+public class SearchResultViewModel extends ViewModel {
+ public SearchResultViewModel() {
+ super("searchResult");
+ setState(new SearchResultState());
+ }
+}
diff --git a/src/main/java/interface_adapter/alert_pop/AlertPopController.java b/src/main/java/interface_adapter/alert_pop/AlertPopController.java
new file mode 100644
index 000000000..a29efa893
--- /dev/null
+++ b/src/main/java/interface_adapter/alert_pop/AlertPopController.java
@@ -0,0 +1,25 @@
+package interface_adapter.alert_pop;
+
+import use_case.note.alert_pop.AlertPopInputBoundary;
+import use_case.note.alert_pop.AlertPopInputData;
+
+/**
+ * The controller for the Alert Pop Use Case.
+ */
+public class AlertPopController {
+ private final AlertPopInputBoundary alertPopInteractor;
+
+ public AlertPopController(AlertPopInputBoundary alertPopInteractor) {
+ this.alertPopInteractor = alertPopInteractor;
+ }
+
+ /**
+ * Executes the Alert Pop Use Case.
+ * @param cityName the name of the user's city.
+ */
+ public void execute(String cityName) {
+ final AlertPopInputData alertPopInputData = new AlertPopInputData(cityName);
+
+ alertPopInteractor.execute(alertPopInputData);
+ }
+}
diff --git a/src/main/java/interface_adapter/alert_pop/AlertPopPresenter.java b/src/main/java/interface_adapter/alert_pop/AlertPopPresenter.java
new file mode 100644
index 000000000..7efaadd3d
--- /dev/null
+++ b/src/main/java/interface_adapter/alert_pop/AlertPopPresenter.java
@@ -0,0 +1,30 @@
+package interface_adapter.alert_pop;
+
+import interface_adapter.weather.WeatherViewModel;
+import use_case.note.alert_pop.AlertPopOutputBoundary;
+import use_case.note.alert_pop.AlertPopOutputData;
+
+/**
+ * This is the Presenter for the ALERT POP use case.
+ */
+public class AlertPopPresenter implements AlertPopOutputBoundary {
+ private final WeatherViewModel viewModel;
+
+ public AlertPopPresenter(WeatherViewModel viewModel) {
+ this.viewModel = viewModel;
+ }
+
+ @Override
+ public void prepareSuccessView(AlertPopOutputData alertPopOutputData) {
+
+ viewModel.getState().setAlert(alertPopOutputData.getAlert());
+ viewModel.firePropertyChanged();
+ }
+
+ @Override
+ public void prepareFailView(String message) {
+ viewModel.getState().setAlert(message);
+ viewModel.firePropertyChanged();
+ }
+
+}
diff --git a/src/main/java/interface_adapter/converter/ConverterController.java b/src/main/java/interface_adapter/converter/ConverterController.java
new file mode 100644
index 000000000..5fe32b4a4
--- /dev/null
+++ b/src/main/java/interface_adapter/converter/ConverterController.java
@@ -0,0 +1,27 @@
+package interface_adapter.converter;
+
+import entity.Weather;
+import use_case.note.convert_farenheit.ConvertFarenheitInputBoundary;
+import use_case.note.convert_farenheit.ConvertFarenheitInputData;
+
+/**
+ * Controller for the convert case.
+ */
+public class ConverterController {
+ private final ConvertFarenheitInputBoundary convertInteractor;
+
+ public ConverterController(ConvertFarenheitInputBoundary cInteractor) {
+ this.convertInteractor = cInteractor;
+ }
+
+ /**
+ * Executes the convert case.
+ * @param weather the note to be recorded
+ */
+ public void execute(Weather weather) {
+ final ConvertFarenheitInputData inputData = new ConvertFarenheitInputData(weather);
+ System.out.println("@ controller going to interactor");
+ convertInteractor.executeConvert(inputData);
+
+ }
+}
diff --git a/src/main/java/interface_adapter/converter/ConverterPresenter.java b/src/main/java/interface_adapter/converter/ConverterPresenter.java
new file mode 100644
index 000000000..7ee80c653
--- /dev/null
+++ b/src/main/java/interface_adapter/converter/ConverterPresenter.java
@@ -0,0 +1,28 @@
+package interface_adapter.converter;
+
+import interface_adapter.weather.WeatherViewModel;
+import use_case.note.convert_farenheit.ConvertFarenheitOutputBoundary;
+import use_case.note.convert_farenheit.ConvertFarenheitOutputData;
+/**
+ * The Presenter for the Convert Units Use case.
+ */
+
+public class ConverterPresenter implements ConvertFarenheitOutputBoundary {
+ private final WeatherViewModel viewModel;
+
+ public ConverterPresenter(WeatherViewModel viewModel) {
+ this.viewModel = viewModel;
+ }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+ viewModel.getState().setError(errorMessage);
+ viewModel.firePropertyChanged();
+ }
+
+ @Override
+ public void prepareSuccessView(ConvertFarenheitOutputData outputData) {
+ viewModel.getState().setError(null);
+ viewModel.firePropertyChanged();
+ }
+}
diff --git a/src/main/java/interface_adapter/nearby_list/NearbyListController.java b/src/main/java/interface_adapter/nearby_list/NearbyListController.java
new file mode 100644
index 000000000..aca764c0a
--- /dev/null
+++ b/src/main/java/interface_adapter/nearby_list/NearbyListController.java
@@ -0,0 +1,28 @@
+package interface_adapter.nearby_list;
+
+import use_case.note.nearby_list.NearbyListInputBoundary;
+import use_case.note.nearby_list.NearbyListInputData;
+
+/**
+ * Controller for the nearby list use case.
+ */
+public class NearbyListController {
+ private final NearbyListInputBoundary nearbyListInteractor;
+
+ public NearbyListController(NearbyListInputBoundary nearbyListInteractor) {
+ this.nearbyListInteractor = nearbyListInteractor;
+ }
+
+ /**
+ * Executes the find nearby cities use case given the longitude and latitude of the current location.
+ *
+ * @param longitude the current longitude
+ * @param latitude the current latitude
+ */
+ public void execute(double longitude, double latitude) {
+ // Create a NearbyListInputData object to encapsulate the input data
+ final NearbyListInputData inputData = new NearbyListInputData(longitude, latitude);
+ // Call the use case's execute method with the input data
+ nearbyListInteractor.execute(inputData);
+ }
+}
diff --git a/src/main/java/interface_adapter/nearby_list/NearbyListPresenter.java b/src/main/java/interface_adapter/nearby_list/NearbyListPresenter.java
new file mode 100644
index 000000000..3ebafe363
--- /dev/null
+++ b/src/main/java/interface_adapter/nearby_list/NearbyListPresenter.java
@@ -0,0 +1,38 @@
+package interface_adapter.nearby_list;
+
+import use_case.note.nearby_list.NearbyListOutputBoundary;
+import use_case.note.nearby_list.NearbyListOutputData;
+
+/**
+ * The presenter for the nearby cities use case.
+ */
+public class NearbyListPresenter implements NearbyListOutputBoundary {
+ private final NearbyListViewModel nearbyListViewModel;
+
+ public NearbyListPresenter(NearbyListViewModel nearbyListViewModel) {
+ this.nearbyListViewModel = nearbyListViewModel;
+ }
+
+ /**
+ * Prepares the success view for the NearbyList related Use Cases.
+ *
+ * @param response the output data
+ */
+ @Override
+ public void presentSuccessView(NearbyListOutputData response) {
+ nearbyListViewModel.getState().setCities(response.getCities());
+ nearbyListViewModel.getState().setError(null);
+ nearbyListViewModel.firePropertyChanged("NearbyList");
+ }
+
+ /**
+ * Prepares the failure view for the NearbyList related Use Cases.
+ *
+ * @param errorMessage the explanation of the failure
+ */
+ @Override
+ public void prepareFailView(String errorMessage) {
+ nearbyListViewModel.getState().setError(errorMessage);
+ nearbyListViewModel.firePropertyChanged();
+ }
+}
diff --git a/src/main/java/interface_adapter/nearby_list/NearbyListState.java b/src/main/java/interface_adapter/nearby_list/NearbyListState.java
new file mode 100644
index 000000000..955d9f625
--- /dev/null
+++ b/src/main/java/interface_adapter/nearby_list/NearbyListState.java
@@ -0,0 +1,28 @@
+package interface_adapter.nearby_list;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The state information for the nearby list view model.
+ */
+public class NearbyListState {
+ private String[] cities;
+ private String error;
+
+ public String[] getCities() {
+ return cities;
+ }
+
+ public void setCities(String[] cities) {
+ this.cities = cities;
+ }
+
+ public void setError(String error) {
+ this.error = error;
+ }
+
+ public String getError() {
+ return error;
+ }
+}
diff --git a/src/main/java/interface_adapter/nearby_list/NearbyListViewModel.java b/src/main/java/interface_adapter/nearby_list/NearbyListViewModel.java
new file mode 100644
index 000000000..6bcf53628
--- /dev/null
+++ b/src/main/java/interface_adapter/nearby_list/NearbyListViewModel.java
@@ -0,0 +1,13 @@
+package interface_adapter.nearby_list;
+
+import interface_adapter.ViewModel;
+
+/**
+ * The view model for the nearby list view.
+ */
+public class NearbyListViewModel extends ViewModel {
+ public NearbyListViewModel() {
+ super("nearbyList");
+ setState(new NearbyListState());
+ }
+}
diff --git a/src/main/java/interface_adapter/note/NoteController.java b/src/main/java/interface_adapter/note/NoteController.java
deleted file mode 100644
index e3e5dfb32..000000000
--- a/src/main/java/interface_adapter/note/NoteController.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package interface_adapter.note;
-
-import use_case.note.NoteInputBoundary;
-
-/**
- * Controller for our Note related Use Cases.
- */
-public class NoteController {
-
- private final NoteInputBoundary noteInteractor;
-
- public NoteController(NoteInputBoundary noteInteractor) {
- this.noteInteractor = noteInteractor;
- }
-
- /**
- * Executes the Note related Use Cases.
- * @param note the note to be recorded
- */
- public void execute(String note) {
- if (note != null) {
- noteInteractor.executeSave(note);
- }
- else {
- noteInteractor.executeRefresh();
- }
- }
-}
diff --git a/src/main/java/interface_adapter/note/NotePresenter.java b/src/main/java/interface_adapter/note/NotePresenter.java
deleted file mode 100644
index d4e416165..000000000
--- a/src/main/java/interface_adapter/note/NotePresenter.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package interface_adapter.note;
-
-import use_case.note.NoteOutputBoundary;
-
-/**
- * The presenter for our Note viewing and editing program.
- */
-public class NotePresenter implements NoteOutputBoundary {
-
- private final NoteViewModel noteViewModel;
-
- public NotePresenter(NoteViewModel noteViewModel) {
- this.noteViewModel = noteViewModel;
- }
-
- /**
- * Prepares the success view for the Note related Use Cases.
- *
- * @param note the output data
- */
- @Override
- public void prepareSuccessView(String note) {
- noteViewModel.getState().setNote(note);
- noteViewModel.getState().setError(null);
- noteViewModel.firePropertyChanged();
- }
-
- /**
- * Prepares the failure view for the Note related Use Cases.
- *
- * @param errorMessage the explanation of the failure
- */
- @Override
- public void prepareFailView(String errorMessage) {
- noteViewModel.getState().setError(errorMessage);
- noteViewModel.firePropertyChanged();
- }
-}
diff --git a/src/main/java/interface_adapter/note/NoteState.java b/src/main/java/interface_adapter/note/NoteState.java
deleted file mode 100644
index c5b2234d6..000000000
--- a/src/main/java/interface_adapter/note/NoteState.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package interface_adapter.note;
-
-/**
- * The State for a note.
- * For this example, a note is simplay a string.
- */
-public class NoteState {
- private String note = "";
- private String error;
-
- public String getNote() {
- return note;
- }
-
- public void setNote(String note) {
- this.note = note;
- }
-
- public void setError(String errorMessage) {
- this.error = errorMessage;
- }
-
- public String getError() {
- return error;
- }
-}
diff --git a/src/main/java/interface_adapter/note/NoteViewModel.java b/src/main/java/interface_adapter/note/NoteViewModel.java
deleted file mode 100644
index 6e185d0fa..000000000
--- a/src/main/java/interface_adapter/note/NoteViewModel.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package interface_adapter.note;
-
-import interface_adapter.ViewModel;
-
-/**
- * The ViewModel for the NoteView.
- */
-public class NoteViewModel extends ViewModel {
- public NoteViewModel() {
- super("note");
- setState(new NoteState());
- }
-}
diff --git a/src/main/java/interface_adapter/weather/WeatherController.java b/src/main/java/interface_adapter/weather/WeatherController.java
new file mode 100644
index 000000000..526c4e607
--- /dev/null
+++ b/src/main/java/interface_adapter/weather/WeatherController.java
@@ -0,0 +1,25 @@
+package interface_adapter.weather;
+
+import use_case.note.search_return.SearchReturnInputBoundary;
+import use_case.note.search_return.SearchReturnInputData;
+
+/**
+ * Controller for our weather related Use Cases.
+ */
+public class WeatherController {
+
+ private final SearchReturnInputBoundary searchInteractor;
+
+ public WeatherController(SearchReturnInputBoundary searchInteractor) {
+ this.searchInteractor = searchInteractor;
+ }
+
+ /**
+ * Execute the weather use case.
+ * @param cityName the city to find the weather for
+ */
+ public void execute(String cityName) {
+ final SearchReturnInputData searchInputData = new SearchReturnInputData(cityName);
+ searchInteractor.execute(searchInputData);
+ }
+}
diff --git a/src/main/java/interface_adapter/weather/WeatherPresenter.java b/src/main/java/interface_adapter/weather/WeatherPresenter.java
new file mode 100644
index 000000000..9a610d8ff
--- /dev/null
+++ b/src/main/java/interface_adapter/weather/WeatherPresenter.java
@@ -0,0 +1,39 @@
+package interface_adapter.weather;
+
+import use_case.note.search_return.SearchReturnOutputBoundary;
+import use_case.note.search_return.SearchReturnOutputData;
+
+/**
+ * The presenter for the weather use case.
+ */
+public class WeatherPresenter implements SearchReturnOutputBoundary {
+
+ private final WeatherViewModel weatherViewModel;
+
+ public WeatherPresenter(WeatherViewModel weatherViewModel) {
+ this.weatherViewModel = weatherViewModel;
+ }
+
+ /**
+ * Prepares the success view for the Weather related Use Cases.
+ *
+ * @param response the output data
+ */
+ @Override
+ public void presentSuccessView(SearchReturnOutputData response) {
+ weatherViewModel.getState().setWeather(response.getWeather());
+ weatherViewModel.getState().setError(null);
+ weatherViewModel.firePropertyChanged("Weather");
+ }
+
+ /**
+ * Prepares the failure view for the Weather related Use Cases.
+ *
+ * @param errorMessage the explanation of the failure
+ */
+ @Override
+ public void prepareFailView(String errorMessage) {
+ weatherViewModel.getState().setError(errorMessage);
+ weatherViewModel.firePropertyChanged();
+ }
+}
diff --git a/src/main/java/interface_adapter/weather/WeatherState.java b/src/main/java/interface_adapter/weather/WeatherState.java
new file mode 100644
index 000000000..4d853b065
--- /dev/null
+++ b/src/main/java/interface_adapter/weather/WeatherState.java
@@ -0,0 +1,36 @@
+package interface_adapter.weather;
+
+import entity.Weather;
+
+/**
+ * The state information for the weather view model.
+ */
+public class WeatherState {
+ private Weather weather;
+ private String error;
+ private String alert;
+
+ public Weather getWeather() {
+ return weather;
+ }
+
+ public String getAlert() {
+ return alert;
+ }
+
+ public void setWeather(Weather weather) {
+ this.weather = weather;
+ }
+
+ public void setError(String errorMessage) {
+ this.error = errorMessage;
+ }
+
+ public void setAlert(String alert) {
+ this.alert = alert;
+ }
+
+ public String getError() {
+ return error;
+ }
+}
diff --git a/src/main/java/interface_adapter/weather/WeatherViewModel.java b/src/main/java/interface_adapter/weather/WeatherViewModel.java
new file mode 100644
index 000000000..5bc58f127
--- /dev/null
+++ b/src/main/java/interface_adapter/weather/WeatherViewModel.java
@@ -0,0 +1,13 @@
+package interface_adapter.weather;
+
+import interface_adapter.ViewModel;
+
+/**
+ * The ViewModel for the WeatherView.
+ */
+public class WeatherViewModel extends ViewModel {
+ public WeatherViewModel() {
+ super("weather");
+ setState(new WeatherState());
+ }
+}
diff --git a/src/main/java/use_case/note/CompareCities/CompareCitiesDataAccessInterface.java b/src/main/java/use_case/note/CompareCities/CompareCitiesDataAccessInterface.java
new file mode 100644
index 000000000..097c2b4a8
--- /dev/null
+++ b/src/main/java/use_case/note/CompareCities/CompareCitiesDataAccessInterface.java
@@ -0,0 +1,45 @@
+package use_case.note.CompareCities;
+
+import entity.Weather;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Interface for the Comparing Cities Use case.
+ */
+public interface CompareCitiesDataAccessInterface {
+ /**
+ * Check if City exists.
+ * @param cityName the weather is displayed for
+ * @return true if city exists
+ * @throws IOException if the city does not exist or oi.
+ */
+ boolean isCityExist(String cityName);
+
+ /**
+ * Creates the Weather.
+ * @param cityname the weather is displayed for
+ * @return the weather information
+ * @throws IOException if the city does not exist or oi.
+ */
+ Weather getWeather(String cityname) throws IOException;
+
+ /**
+ * Saves the Weather.
+ * @param weather save weather infor of a city to a map. since there is method getCityname in Weather Object.
+ * this method can work without have cityname as its input.
+ */
+ void saveWeatherinfor(Weather weather);
+
+ /**
+ * Returns the City.
+ * @return the city
+ * */
+ Map getcitytoweather();
+
+ /**
+ * This method "clean" the elements inside this.citytoweather we don't want to accumulate pairs.
+ */
+ void clearcitytoweather();
+}
diff --git a/src/main/java/use_case/note/CompareCities/CompareCitiesInputBoundary.java b/src/main/java/use_case/note/CompareCities/CompareCitiesInputBoundary.java
new file mode 100644
index 000000000..38a0d6435
--- /dev/null
+++ b/src/main/java/use_case/note/CompareCities/CompareCitiesInputBoundary.java
@@ -0,0 +1,5 @@
+package use_case.note.CompareCities;
+
+public interface CompareCitiesInputBoundary {
+ void execute(CompareCitiesInputData compareCitiesInputData);
+}
diff --git a/src/main/java/use_case/note/CompareCities/CompareCitiesInputData.java b/src/main/java/use_case/note/CompareCities/CompareCitiesInputData.java
new file mode 100644
index 000000000..6b976f2e4
--- /dev/null
+++ b/src/main/java/use_case/note/CompareCities/CompareCitiesInputData.java
@@ -0,0 +1,20 @@
+package use_case.note.CompareCities;
+
+public class CompareCitiesInputData {
+
+ private final String firstcityname;
+ private final String secondcityname;
+
+ public CompareCitiesInputData(String firstcityname, String secondcityname) {
+ this.firstcityname = firstcityname;
+ this.secondcityname = secondcityname;
+ }
+
+ public String getFirstcityname() {
+ return firstcityname;
+ }
+
+ public String getSecondcityname() {
+ return secondcityname;
+ }
+}
diff --git a/src/main/java/use_case/note/CompareCities/CompareCitiesInteractor.java b/src/main/java/use_case/note/CompareCities/CompareCitiesInteractor.java
new file mode 100644
index 000000000..e0c7485b5
--- /dev/null
+++ b/src/main/java/use_case/note/CompareCities/CompareCitiesInteractor.java
@@ -0,0 +1,58 @@
+package use_case.note.CompareCities;
+
+import entity.Weather;
+
+import java.io.IOException;
+
+/*
+* The Comparecities Interactor.
+ */
+public class CompareCitiesInteractor implements CompareCitiesInputBoundary {
+ private final CompareCitiesDataAccessInterface compareCitiesDataAccessInterface;
+ private final CompareCitiesOutputBoundary comparecitiesPresenter;
+
+ public CompareCitiesInteractor(CompareCitiesDataAccessInterface compareCitiesDataAccessInterface,
+ CompareCitiesOutputBoundary comparecitiesPresenter) {
+ this.comparecitiesPresenter = comparecitiesPresenter;
+ this.compareCitiesDataAccessInterface = compareCitiesDataAccessInterface;
+ }
+
+ /**
+ * @param compareCitiesInputData this input data has 2 city names inside it.
+ * When execute by controller, call presenter to prepare fail or success view. If success, make a new an output data
+ * and put it into presenter.
+ * */
+ public void execute(CompareCitiesInputData compareCitiesInputData) {
+ final String firstcityname = compareCitiesInputData.getFirstcityname();
+ final String secondcityname = compareCitiesInputData.getSecondcityname();
+ if (firstcityname.equals(secondcityname)) {
+ comparecitiesPresenter.prepareFailView("Cannot compare the same city");
+ }
+ else {
+ if (!compareCitiesDataAccessInterface.isCityExist(firstcityname)
+ || !compareCitiesDataAccessInterface.isCityExist(secondcityname)) {
+ comparecitiesPresenter.prepareFailView("city not found");
+ }
+ else {
+ // THE try-catch statement below is to check firstcityname, secondcityname have corresponding weathers.
+ try {
+ final Weather firstweather = compareCitiesDataAccessInterface.getWeather(firstcityname);
+ compareCitiesDataAccessInterface.saveWeatherinfor(firstweather);
+
+ final Weather secondweather = compareCitiesDataAccessInterface.getWeather(secondcityname);
+ compareCitiesDataAccessInterface.saveWeatherinfor(secondweather);
+ final CompareCitiesOutPutData compareCitiesOutPutData = new CompareCitiesOutPutData(firstcityname,
+ (Weather) compareCitiesDataAccessInterface.getcitytoweather().get(firstcityname),
+ secondcityname,
+ (Weather) compareCitiesDataAccessInterface.getcitytoweather().get(secondcityname), false);
+ comparecitiesPresenter.prepareSuccessView(compareCitiesOutPutData);
+ // After each round of execution, clear map in DAO.
+ compareCitiesDataAccessInterface.clearcitytoweather();
+ }
+ catch (IOException ioException) {
+ comparecitiesPresenter.prepareFailView("Network error while fetching weather data for " + firstcityname + secondcityname);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/use_case/note/CompareCities/CompareCitiesOutPutData.java b/src/main/java/use_case/note/CompareCities/CompareCitiesOutPutData.java
new file mode 100644
index 000000000..2104db263
--- /dev/null
+++ b/src/main/java/use_case/note/CompareCities/CompareCitiesOutPutData.java
@@ -0,0 +1,44 @@
+package use_case.note.CompareCities;
+
+import entity.Weather;
+
+public class CompareCitiesOutPutData {
+ private final String firstCityname;
+ private final String secondCityname;
+ private Weather firstWeather;
+ private Weather secondWeather;
+ private final boolean useCaseFailed;
+
+ public CompareCitiesOutPutData(String firstCityname, Weather firstWeather,
+ String secondCityname, Weather secondWeather, boolean useCaseFailed) {
+ this.firstCityname = firstCityname;
+ this.secondCityname = secondCityname;
+ this.setFirstWeather(firstWeather);
+ this.setSecondWeather(secondWeather);
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public String getFirstCityname() {
+ return firstCityname;
+ }
+
+ public String getSecondCityname() {
+ return secondCityname;
+ }
+
+ public Weather getFirstWeather() {
+ return firstWeather;
+ }
+
+ public void setFirstWeather(Weather firstWeather) {
+ this.firstWeather = firstWeather;
+ }
+
+ public Weather getSecondWeather() {
+ return secondWeather;
+ }
+
+ public void setSecondWeather(Weather secondWeather) {
+ this.secondWeather = secondWeather;
+ }
+}
diff --git a/src/main/java/use_case/note/CompareCities/CompareCitiesOutputBoundary.java b/src/main/java/use_case/note/CompareCities/CompareCitiesOutputBoundary.java
new file mode 100644
index 000000000..55d49c082
--- /dev/null
+++ b/src/main/java/use_case/note/CompareCities/CompareCitiesOutputBoundary.java
@@ -0,0 +1,15 @@
+package use_case.note.CompareCities;
+
+public interface CompareCitiesOutputBoundary {
+ /**
+ * Prepares the success view for the Login Use Case.
+ * @param outputData the output data
+ */
+ void prepareSuccessView(CompareCitiesOutPutData outputData);
+
+ /**
+ * Prepares the failure view for the Login Use Case.
+ * @param errorMessage the explanation of the failure
+ */
+ void prepareFailView(String errorMessage);
+}
diff --git a/src/main/java/use_case/note/HistoricalWeatherDataAccessInterface.java b/src/main/java/use_case/note/HistoricalWeatherDataAccessInterface.java
new file mode 100644
index 000000000..6ce05f46f
--- /dev/null
+++ b/src/main/java/use_case/note/HistoricalWeatherDataAccessInterface.java
@@ -0,0 +1,28 @@
+package use_case.note;
+
+import java.io.IOException;
+
+import entity.Weather;
+
+/**
+ * Interface for the HistoricalWeatherDataAccessObject. It consists of methods for
+ * saving the weather.
+ */
+public interface HistoricalWeatherDataAccessInterface {
+ /**
+ * Saves the weather data.
+ * @param weather the weather data to save
+ * @param timstamp the timestamp of the weather data
+ * @throws IOException if there is an error saving the weather data
+ */
+ void saveWeather(Weather weather, String timstamp) throws IOException;
+
+ /**
+ * Gets the weather data.
+ * @param city the city to get the weather data for
+ * @param timestamp the timestamp of the weather data
+ * @return Weather object if no error.
+ * @throws IOException if there is an error getting the weather data
+ */
+ Weather getWeather(String city, String timestamp) throws IOException;
+}
diff --git a/src/main/java/use_case/note/NoteDataAccessInterface.java b/src/main/java/use_case/note/NoteDataAccessInterface.java
deleted file mode 100644
index b71597828..000000000
--- a/src/main/java/use_case/note/NoteDataAccessInterface.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package use_case.note;
-
-import entity.User;
-
-/**
- * Interface for the NoteDAO. It consists of methods for
- * both loading and saving a note.
- */
-public interface NoteDataAccessInterface {
-
- /**
- * Saves a note for a given user. This will replace any existing note.
- * The password of the user must match that of the user saved in the system.
- * @param user the user information associated with the note
- * @param note the note to be saved
- * @return the contents of the note
- * @throws DataAccessException if the user's note can not be saved for any reason
- */
- String saveNote(User user, String note) throws DataAccessException;
-
- /**
- * Returns the note associated with the user. The password
- * is not checked, so anyone can read the information.
- * @param user the user information associated with the note
- * @return the contents of the note
- * @throws DataAccessException if the user's note can not be loaded for any reason
- */
- String loadNote(User user) throws DataAccessException;
-
-}
diff --git a/src/main/java/use_case/note/NoteInputBoundary.java b/src/main/java/use_case/note/NoteInputBoundary.java
deleted file mode 100644
index b41da9bf5..000000000
--- a/src/main/java/use_case/note/NoteInputBoundary.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package use_case.note;
-
-/**
- * The Input Boundary for our note-related use cases. Since they are closely related,
- * we have included them both in the same interface for simplicity.
- */
-public interface NoteInputBoundary {
-
- /**
- * Executes the refresh note use case.
- */
- void executeRefresh();
-
- /**
- * Executes the save note use case.
- * @param message the input data
- */
- void executeSave(String message);
-}
diff --git a/src/main/java/use_case/note/NoteInteractor.java b/src/main/java/use_case/note/NoteInteractor.java
deleted file mode 100644
index 369e9309a..000000000
--- a/src/main/java/use_case/note/NoteInteractor.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package use_case.note;
-
-import entity.User;
-
-/**
- * The "Use Case Interactor" for our two note-related use cases of refreshing
- * the contents of the note and saving the contents of the note. Since they
- * are closely related, we have combined them here for simplicity.
- */
-public class NoteInteractor implements NoteInputBoundary {
-
- private final NoteDataAccessInterface noteDataAccessInterface;
- private final NoteOutputBoundary noteOutputBoundary;
- // Note: this program has it hardcoded which user object it is getting data for;
- // you could change this if you wanted to generalize the code. For example,
- // you might allow a user of the program to create a new note, which you
- // could store as a "user" through the API OR you might maintain all notes
- // in a JSON object stored in one common "user" stored through the API.
- private final User user = new User("jonathan_calver2", "abc123");
-
- public NoteInteractor(NoteDataAccessInterface noteDataAccessInterface,
- NoteOutputBoundary noteOutputBoundary) {
- this.noteDataAccessInterface = noteDataAccessInterface;
- this.noteOutputBoundary = noteOutputBoundary;
- }
-
- /**
- * Executes the refresh note use case.
- *
- */
- @Override
- public void executeRefresh() {
- try {
-
- final String note = noteDataAccessInterface.loadNote(user);
- noteOutputBoundary.prepareSuccessView(note);
- }
- catch (DataAccessException ex) {
- noteOutputBoundary.prepareFailView(ex.getMessage());
- }
- }
-
- /**
- * Executes the save note use case.
- *
- * @param note the input data
- */
- @Override
- public void executeSave(String note) {
- try {
-
- final String updatedNote = noteDataAccessInterface.saveNote(user, note);
- noteOutputBoundary.prepareSuccessView(updatedNote);
- }
- catch (DataAccessException ex) {
- noteOutputBoundary.prepareFailView(ex.getMessage());
- }
- }
-}
diff --git a/src/main/java/use_case/note/NoteOutputBoundary.java b/src/main/java/use_case/note/NoteOutputBoundary.java
deleted file mode 100644
index c0c2bb1d0..000000000
--- a/src/main/java/use_case/note/NoteOutputBoundary.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package use_case.note;
-
-/**
- * The output boundary for the Login Use Case.
- */
-public interface NoteOutputBoundary {
- /**
- * Prepares the success view for the Note related Use Cases.
- * @param message the output data
- */
- void prepareSuccessView(String message);
-
- /**
- * Prepares the failure view for the Note related Use Cases.
- * @param errorMessage the explanation of the failure
- */
- void prepareFailView(String errorMessage);
-}
diff --git a/src/main/java/use_case/note/WeatherDataAccessInterface.java b/src/main/java/use_case/note/WeatherDataAccessInterface.java
new file mode 100644
index 000000000..44f758b4b
--- /dev/null
+++ b/src/main/java/use_case/note/WeatherDataAccessInterface.java
@@ -0,0 +1,20 @@
+package use_case.note;
+
+import entity.Weather;
+import java.io.IOException;
+
+/**
+ * Interface for the WeatherDAO. It consists of methods for
+ * loading the weather.
+ */
+public interface WeatherDataAccessInterface {
+
+ /**
+ * Creates the Weather.
+ * @param city the weather is displayed for
+ * @return the weather information
+ * @throws IOException if the city does not exist or oi.
+ */
+ public Weather getWeather(String city) throws IOException;
+
+}
diff --git a/src/main/java/use_case/note/alert_pop/AlertPopInputBoundary.java b/src/main/java/use_case/note/alert_pop/AlertPopInputBoundary.java
new file mode 100644
index 000000000..0c9d44bd9
--- /dev/null
+++ b/src/main/java/use_case/note/alert_pop/AlertPopInputBoundary.java
@@ -0,0 +1,9 @@
+package use_case.note.alert_pop;
+
+/**
+ * No input because it is Automatic.
+ */
+public interface AlertPopInputBoundary {
+
+ void execute(AlertPopInputData alertPopInputData);
+}
diff --git a/src/main/java/use_case/note/alert_pop/AlertPopInputData.java b/src/main/java/use_case/note/alert_pop/AlertPopInputData.java
new file mode 100644
index 000000000..2bb4bb6e1
--- /dev/null
+++ b/src/main/java/use_case/note/alert_pop/AlertPopInputData.java
@@ -0,0 +1,14 @@
+package use_case.note.alert_pop;
+
+// No input because alertpop is automatic.
+public class AlertPopInputData {
+ private final String cityName;
+
+ public AlertPopInputData(String cityName) {
+ this.cityName = cityName;
+ }
+
+ public String getCityName() {
+ return cityName;
+ }
+}
diff --git a/src/main/java/use_case/note/alert_pop/AlertPopInteractor.java b/src/main/java/use_case/note/alert_pop/AlertPopInteractor.java
new file mode 100644
index 000000000..b68661b04
--- /dev/null
+++ b/src/main/java/use_case/note/alert_pop/AlertPopInteractor.java
@@ -0,0 +1,35 @@
+package use_case.note.alert_pop;
+
+import use_case.note.WeatherDataAccessInterface;
+import entity.Weather;
+
+public class AlertPopInteractor implements AlertPopInputBoundary {
+ private final WeatherDataAccessInterface weatherAccess;
+ private final AlertPopOutputBoundary outputBoundary;
+
+ public AlertPopInteractor(WeatherDataAccessInterface weatherAccess, AlertPopOutputBoundary outputBoundary) {
+ this.weatherAccess = weatherAccess;
+ this.outputBoundary = outputBoundary;
+ }
+
+ @Override
+ public void execute(AlertPopInputData alertPopInputData) {
+ try {
+ String cityName = alertPopInputData.getCityName();
+ Weather weather = weatherAccess.getWeather(cityName);
+ String alert = weather.getDescription();
+ String noAlert = "no weather alert";
+
+ if (noAlert.equals(alert)) {
+ AlertPopOutputData outputData = new AlertPopOutputData(noAlert, false);
+ outputBoundary.prepareSuccessView(outputData);
+ } else {
+ AlertPopOutputData outputData = new AlertPopOutputData(alert, false);
+ outputBoundary.prepareSuccessView(outputData);
+ }
+ } catch (Exception e) {
+ outputBoundary.prepareFailView("Failed to retrieve weather data: " + e.getMessage());
+ }
+ }
+}
+
diff --git a/src/main/java/use_case/note/alert_pop/AlertPopOutputBoundary.java b/src/main/java/use_case/note/alert_pop/AlertPopOutputBoundary.java
new file mode 100644
index 000000000..4e431ba03
--- /dev/null
+++ b/src/main/java/use_case/note/alert_pop/AlertPopOutputBoundary.java
@@ -0,0 +1,8 @@
+package use_case.note.alert_pop;
+
+public interface AlertPopOutputBoundary {
+
+ void prepareSuccessView(AlertPopOutputData alertPopOutputData);
+
+ void prepareFailView(String errorMessage);
+}
diff --git a/src/main/java/use_case/note/alert_pop/AlertPopOutputData.java b/src/main/java/use_case/note/alert_pop/AlertPopOutputData.java
new file mode 100644
index 000000000..50f45b31e
--- /dev/null
+++ b/src/main/java/use_case/note/alert_pop/AlertPopOutputData.java
@@ -0,0 +1,20 @@
+package use_case.note.alert_pop;
+
+public class AlertPopOutputData {
+
+ private final String alert;
+ private final boolean useCaseFailed;
+
+ public AlertPopOutputData(String alert, boolean useCaseFailed) {
+ this.alert = alert;
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public String getAlert() {
+ return alert;
+ }
+
+ public boolean isUseCaseFailed() {
+ return useCaseFailed;
+ }
+}
diff --git a/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitInputBoundary.java b/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitInputBoundary.java
new file mode 100644
index 000000000..8de3c12a4
--- /dev/null
+++ b/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitInputBoundary.java
@@ -0,0 +1,7 @@
+package use_case.note.convert_farenheit;
+
+public interface ConvertFarenheitInputBoundary {
+ void executeConvert(ConvertFarenheitInputData convertFarenheitInputData);
+
+ void convert(ConvertFarenheitInputData convertFarenheitInputData);
+}
diff --git a/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitInputData.java b/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitInputData.java
new file mode 100644
index 000000000..3b463e735
--- /dev/null
+++ b/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitInputData.java
@@ -0,0 +1,11 @@
+package use_case.note.convert_farenheit;
+
+import entity.Weather;
+
+public class ConvertFarenheitInputData {
+ public Weather weather;
+
+ public ConvertFarenheitInputData(Weather weather) {
+ this.weather = weather;
+ }
+}
diff --git a/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitOutputBoundary.java b/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitOutputBoundary.java
new file mode 100644
index 000000000..a2c9f7018
--- /dev/null
+++ b/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitOutputBoundary.java
@@ -0,0 +1,7 @@
+package use_case.note.convert_farenheit;
+
+public interface ConvertFarenheitOutputBoundary {
+ void prepareFailView(String errorMessage);
+
+ void prepareSuccessView(ConvertFarenheitOutputData outputData);
+}
diff --git a/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitOutputData.java b/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitOutputData.java
new file mode 100644
index 000000000..0f1c7f201
--- /dev/null
+++ b/src/main/java/use_case/note/convert_farenheit/ConvertFarenheitOutputData.java
@@ -0,0 +1,21 @@
+package use_case.note.convert_farenheit;
+
+import entity.Weather;
+
+public class ConvertFarenheitOutputData {
+ private final boolean useCaseFailed;
+ private final Weather weather;
+
+ public ConvertFarenheitOutputData(Weather weather, boolean useCaseFailed) {
+ this.useCaseFailed = useCaseFailed;
+ this.weather = weather;
+ }
+
+ public boolean isUseCaseFailed() {
+ return useCaseFailed;
+ }
+
+ public Weather getWeather() {
+ return weather;
+ }
+}
diff --git a/src/main/java/use_case/note/convert_farenheit/ConvertInteractor.java b/src/main/java/use_case/note/convert_farenheit/ConvertInteractor.java
new file mode 100644
index 000000000..f060153b8
--- /dev/null
+++ b/src/main/java/use_case/note/convert_farenheit/ConvertInteractor.java
@@ -0,0 +1,66 @@
+package use_case.note.convert_farenheit;
+
+public class ConvertInteractor implements ConvertFarenheitInputBoundary {
+ public static final double MPS_MPH = 2.237;
+ public static final double CELC_FAREN = 1.8;
+ public static final double FAREN_ADD = 32;
+ private static final int ABSOLUTEZERO = -500;
+
+ private final ConvertFarenheitOutputBoundary oBounds;
+
+ public ConvertInteractor(ConvertFarenheitOutputBoundary convertFarenheitOutputBoundary) {
+ this.oBounds = convertFarenheitOutputBoundary;
+ }
+
+ @Override
+ public void executeConvert(ConvertFarenheitInputData cInputData) {
+
+ if (cInputData.weather != null) {
+
+ convert(cInputData);
+ System.out.println(" @ the interactor about to show view");
+
+ final ConvertFarenheitOutputData outputData = new ConvertFarenheitOutputData(cInputData.weather, false);
+ oBounds.prepareSuccessView(outputData);
+ }
+ else {
+
+ oBounds.prepareFailView("UNABLE TO CONVERT");
+ }
+ }
+
+ @Override
+ public void convert(ConvertFarenheitInputData cInputData) {
+
+ final int temp = (int) Math.floor(cInputData.weather.getTemperature() * CELC_FAREN + FAREN_ADD);
+
+ final int speed = (int) Math.floor(cInputData.weather.getWindSpeed() * MPS_MPH);
+
+ if (cInputData.weather.getfaren() == ABSOLUTEZERO) {
+ // set instance variables
+ System.out.println("intial setting");
+ cInputData.weather.setfarenheit(temp);
+ cInputData.weather.setMiles(speed);
+
+ // update view
+ System.out.println("updated view");
+ cInputData.weather.setWindSpeed(speed);
+ cInputData.weather.setTemperature(temp);
+
+ cInputData.weather.setMetric(false);
+ }
+ else if (cInputData.weather.isMetric()) {
+
+ cInputData.weather.setWindSpeed(cInputData.weather.getMiles());
+ cInputData.weather.setTemperature(cInputData.weather.getfaren());
+ cInputData.weather.setMetric(false);
+ }
+ else {
+ cInputData.weather.setWindSpeed(cInputData.weather.getKilometers());
+ cInputData.weather.setTemperature(cInputData.weather.getCelcius());
+ cInputData.weather.setMetric(true);
+ }
+ }
+
+}
+
diff --git a/src/main/java/use_case/note/nearby_list/NearbyCitiesAccessInterface.java b/src/main/java/use_case/note/nearby_list/NearbyCitiesAccessInterface.java
new file mode 100644
index 000000000..5bb172f4c
--- /dev/null
+++ b/src/main/java/use_case/note/nearby_list/NearbyCitiesAccessInterface.java
@@ -0,0 +1,11 @@
+package use_case.note.nearby_list;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Interface for generating list of nearby cities.
+ */
+public interface NearbyCitiesAccessInterface {
+ List getNearbyCities(double latitude, double longitude) throws IOException;
+}
diff --git a/src/main/java/use_case/note/nearby_list/NearbyListInputBoundary.java b/src/main/java/use_case/note/nearby_list/NearbyListInputBoundary.java
new file mode 100644
index 000000000..a52c201a0
--- /dev/null
+++ b/src/main/java/use_case/note/nearby_list/NearbyListInputBoundary.java
@@ -0,0 +1,14 @@
+package use_case.note.nearby_list;
+
+/**
+ * An input is when the map moves.
+ */
+public interface NearbyListInputBoundary {
+
+ /**
+ * Executes the nearby list use case.
+ *
+ * @param nearbyListInputData the input data for the nearby list use case.
+ */
+ void execute(NearbyListInputData nearbyListInputData);
+}
diff --git a/src/main/java/use_case/note/nearby_list/NearbyListInputData.java b/src/main/java/use_case/note/nearby_list/NearbyListInputData.java
new file mode 100644
index 000000000..60748d6ac
--- /dev/null
+++ b/src/main/java/use_case/note/nearby_list/NearbyListInputData.java
@@ -0,0 +1,22 @@
+package use_case.note.nearby_list;
+
+/**
+ * An input is when the map moves.
+ */
+public class NearbyListInputData {
+ private final double longitude;
+ private final double latitude;
+
+ public NearbyListInputData(double longitude, double latitude) {
+ this.longitude = longitude;
+ this.latitude = latitude;
+ }
+
+ public double getLongitude() {
+ return longitude;
+ }
+
+ public double getLatitude() {
+ return latitude;
+ }
+}
diff --git a/src/main/java/use_case/note/nearby_list/NearbyListInteractor.java b/src/main/java/use_case/note/nearby_list/NearbyListInteractor.java
new file mode 100644
index 000000000..cc403fe1a
--- /dev/null
+++ b/src/main/java/use_case/note/nearby_list/NearbyListInteractor.java
@@ -0,0 +1,31 @@
+package use_case.note.nearby_list;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * The interactor for the nearby cities use case.
+ */
+public class NearbyListInteractor implements NearbyListInputBoundary {
+ private final NearbyListOutputBoundary outputBoundary;
+ private final NearbyCitiesAccessInterface cityDataAccess;
+
+ public NearbyListInteractor(NearbyListOutputBoundary outputBoundary, NearbyCitiesAccessInterface cityDataAccess) {
+ this.outputBoundary = outputBoundary;
+ this.cityDataAccess = cityDataAccess;
+ }
+
+ @Override
+ public void execute(NearbyListInputData nearbyListInputData) {
+ try {
+ final double latitude = nearbyListInputData.getLatitude();
+ final double longitude = nearbyListInputData.getLongitude();
+ final String[] cities = cityDataAccess.getNearbyCities(latitude, longitude).toArray(new String[0]);
+ final NearbyListOutputData outputData = new NearbyListOutputData(cities, false);
+ outputBoundary.presentSuccessView(outputData);
+ }
+ catch (IOException exception) {
+ outputBoundary.prepareFailView("Error: " + exception.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/use_case/note/nearby_list/NearbyListOutputBoundary.java b/src/main/java/use_case/note/nearby_list/NearbyListOutputBoundary.java
new file mode 100644
index 000000000..4ac7b4bd4
--- /dev/null
+++ b/src/main/java/use_case/note/nearby_list/NearbyListOutputBoundary.java
@@ -0,0 +1,8 @@
+package use_case.note.nearby_list;
+
+public interface NearbyListOutputBoundary {
+
+ void presentSuccessView(NearbyListOutputData nearbyListOutputData);
+
+ void prepareFailView(String errorMessage);
+}
diff --git a/src/main/java/use_case/note/nearby_list/NearbyListOutputData.java b/src/main/java/use_case/note/nearby_list/NearbyListOutputData.java
new file mode 100644
index 000000000..0f855ea1b
--- /dev/null
+++ b/src/main/java/use_case/note/nearby_list/NearbyListOutputData.java
@@ -0,0 +1,23 @@
+package use_case.note.nearby_list;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class NearbyListOutputData {
+
+ private final String[] cities;
+ private final boolean useCaseFailed;
+
+ public NearbyListOutputData(String[] cities, boolean useCaseFailed) {
+ this.cities = cities;
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public boolean isUseCaseFailed() {
+ return useCaseFailed;
+ }
+
+ public String[] getCities() {
+ return cities;
+ }
+}
diff --git a/src/main/java/use_case/note/search_result/SearchResultInputBoundary.java b/src/main/java/use_case/note/search_result/SearchResultInputBoundary.java
new file mode 100644
index 000000000..99054de87
--- /dev/null
+++ b/src/main/java/use_case/note/search_result/SearchResultInputBoundary.java
@@ -0,0 +1,8 @@
+package use_case.note.search_result;
+
+// Didn't implement the use case for past 24 hours. Undecided between a separate use case or putting it inside
+// search bar.
+public interface SearchResultInputBoundary {
+
+ void execute(SearchResultInputData searchResultInputData);
+}
diff --git a/src/main/java/use_case/note/search_result/SearchResultInputData.java b/src/main/java/use_case/note/search_result/SearchResultInputData.java
new file mode 100644
index 000000000..f15ab28b1
--- /dev/null
+++ b/src/main/java/use_case/note/search_result/SearchResultInputData.java
@@ -0,0 +1,22 @@
+package use_case.note.search_result;
+
+// Didn't implement the use case for past 24 hours. Undecided between a separate use case or putting it inside
+// search bar.
+public class SearchResultInputData {
+
+ private String city;
+ private final String date;
+
+ public SearchResultInputData(String cityName, String date) {
+ this.city = cityName;
+ this.date = date;
+ }
+
+ public String getCity() {
+ return this.city;
+ }
+
+ public String getDate() {
+ return this.date;
+ }
+}
diff --git a/src/main/java/use_case/note/search_result/SearchResultInteractor.java b/src/main/java/use_case/note/search_result/SearchResultInteractor.java
new file mode 100644
index 000000000..335f99b80
--- /dev/null
+++ b/src/main/java/use_case/note/search_result/SearchResultInteractor.java
@@ -0,0 +1,45 @@
+package use_case.note.search_result;
+
+import java.io.IOException;
+
+import entity.Weather;
+import use_case.note.HistoricalWeatherDataAccessInterface;
+import use_case.note.WeatherDataAccessInterface;
+
+/**
+ * The interactor for the search result use case.
+ */
+public class SearchResultInteractor implements SearchResultInputBoundary {
+ private final SearchResultOutputBoundary outputBoundary;
+ private final WeatherDataAccessInterface weatherDataAccess;
+ private final HistoricalWeatherDataAccessInterface historicalWeatherDataAccessInterface;
+
+ public SearchResultInteractor(SearchResultOutputBoundary outputBoundary,
+ WeatherDataAccessInterface weatherDataAccess, HistoricalWeatherDataAccessInterface
+ historicalDataInterface) {
+ this.outputBoundary = outputBoundary;
+ this.weatherDataAccess = weatherDataAccess;
+ this.historicalWeatherDataAccessInterface = historicalDataInterface;
+ }
+
+ @Override
+ public void execute(SearchResultInputData searchReturnInputData) {
+ try {
+ final String city = searchReturnInputData.getCity();
+ final String timestamp = searchReturnInputData.getDate();
+
+ final Weather historicalWeather = historicalWeatherDataAccessInterface.getWeather(city, timestamp);
+
+ // Send it to the output boundary
+ final SearchResultOutputData outputData =
+ new SearchResultOutputData(historicalWeather, false);
+ outputBoundary.presentSuccessView(outputData);
+
+ }
+ catch (IOException exception) {
+ // Handle exception if weather data retrieval fails and send failure view
+ outputBoundary.presentFailView("Failed to retrieve weather data: " + exception.getMessage());
+ }
+ }
+
+}
diff --git a/src/main/java/use_case/note/search_result/SearchResultOutputBoundary.java b/src/main/java/use_case/note/search_result/SearchResultOutputBoundary.java
new file mode 100644
index 000000000..cee8789c9
--- /dev/null
+++ b/src/main/java/use_case/note/search_result/SearchResultOutputBoundary.java
@@ -0,0 +1,8 @@
+package use_case.note.search_result;
+
+public interface SearchResultOutputBoundary {
+
+ void presentSuccessView(SearchResultOutputData searchResultOutputData);
+
+ void presentFailView(String errorMessage);
+}
diff --git a/src/main/java/use_case/note/search_result/SearchResultOutputData.java b/src/main/java/use_case/note/search_result/SearchResultOutputData.java
new file mode 100644
index 000000000..beb08b3b7
--- /dev/null
+++ b/src/main/java/use_case/note/search_result/SearchResultOutputData.java
@@ -0,0 +1,22 @@
+package use_case.note.search_result;
+
+import entity.Weather;
+
+public class SearchResultOutputData {
+
+ private final Weather weather;
+ private final boolean useCaseFailed;
+
+ public SearchResultOutputData(Weather weather, boolean useCaseFailed) {
+ this.weather = weather;
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public Weather getWeather() {
+ return weather;
+ }
+
+ public boolean isUseCaseFailed() {
+ return useCaseFailed;
+ }
+}
diff --git a/src/main/java/use_case/note/search_return/SearchReturnInputBoundary.java b/src/main/java/use_case/note/search_return/SearchReturnInputBoundary.java
new file mode 100644
index 000000000..ac47665d0
--- /dev/null
+++ b/src/main/java/use_case/note/search_return/SearchReturnInputBoundary.java
@@ -0,0 +1,14 @@
+package use_case.note.search_return;
+
+/**
+ * The input boundary for the search return use case.
+ */
+public interface SearchReturnInputBoundary {
+
+ /**
+ * Execute the search return use case.
+ *
+ * @param searchReturnInputData The input data for the search return use case.
+ */
+ void execute(SearchReturnInputData searchReturnInputData);
+}
diff --git a/src/main/java/use_case/note/search_return/SearchReturnInputData.java b/src/main/java/use_case/note/search_return/SearchReturnInputData.java
new file mode 100644
index 000000000..4c892875c
--- /dev/null
+++ b/src/main/java/use_case/note/search_return/SearchReturnInputData.java
@@ -0,0 +1,16 @@
+package use_case.note.search_return;
+
+/**
+ * // Input is one click on "return."
+ */
+public class SearchReturnInputData {
+ private final String city;
+
+ public SearchReturnInputData(String cityName) {
+ this.city = cityName;
+ }
+
+ public String getCity() {
+ return this.city;
+ }
+}
diff --git a/src/main/java/use_case/note/search_return/SearchReturnInteractor.java b/src/main/java/use_case/note/search_return/SearchReturnInteractor.java
new file mode 100644
index 000000000..ade3f1ecd
--- /dev/null
+++ b/src/main/java/use_case/note/search_return/SearchReturnInteractor.java
@@ -0,0 +1,52 @@
+package use_case.note.search_return;
+
+import entity.Weather;
+import use_case.note.HistoricalWeatherDataAccessInterface;
+import use_case.note.WeatherDataAccessInterface;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * The interactor for the search return use case.
+ */
+public class SearchReturnInteractor implements SearchReturnInputBoundary {
+ private final SearchReturnOutputBoundary outputBoundary;
+ private final WeatherDataAccessInterface weatherDataAccess;
+ private final HistoricalWeatherDataAccessInterface historicalWeatherDataAccessInterface;
+
+ public SearchReturnInteractor(SearchReturnOutputBoundary outputBoundary,
+ WeatherDataAccessInterface weatherDataAccess,
+ HistoricalWeatherDataAccessInterface historicalWeatherDataAccessInterface) {
+ this.outputBoundary = outputBoundary;
+ this.weatherDataAccess = weatherDataAccess;
+ this.historicalWeatherDataAccessInterface = historicalWeatherDataAccessInterface;
+ }
+
+ @Override
+ public void execute(SearchReturnInputData searchReturnInputData) {
+ try {
+ final String city = searchReturnInputData.getCity();
+ // Simulate reading weather data
+ final Weather weatherData = weatherDataAccess.getWeather(city);
+
+ // Store it in historical data
+ final DateTimeFormatter formatter = DateTimeFormatter
+ .ofPattern("yyyy-MM-dd HH").withZone(ZoneOffset.UTC);
+ final String timestamp = formatter.format(Instant.now());
+ historicalWeatherDataAccessInterface.saveWeather(weatherData, timestamp);
+ // Send it to the output boundary
+ final SearchReturnOutputData outputData =
+ new SearchReturnOutputData(weatherData, false);
+ outputBoundary.presentSuccessView(outputData);
+
+ }
+ catch (IOException exception) {
+ // Handle exception if weather data retrieval fails and send failure view
+ outputBoundary.prepareFailView("Failed to retrieve weather data: " + exception.getMessage());
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/use_case/note/search_return/SearchReturnOutputBoundary.java b/src/main/java/use_case/note/search_return/SearchReturnOutputBoundary.java
new file mode 100644
index 000000000..90e7ca950
--- /dev/null
+++ b/src/main/java/use_case/note/search_return/SearchReturnOutputBoundary.java
@@ -0,0 +1,8 @@
+package use_case.note.search_return;
+
+public interface SearchReturnOutputBoundary {
+
+ void presentSuccessView(SearchReturnOutputData searchReturnOutputData);
+
+ void prepareFailView(String errorMessage);
+}
diff --git a/src/main/java/use_case/note/search_return/SearchReturnOutputData.java b/src/main/java/use_case/note/search_return/SearchReturnOutputData.java
new file mode 100644
index 000000000..cce686880
--- /dev/null
+++ b/src/main/java/use_case/note/search_return/SearchReturnOutputData.java
@@ -0,0 +1,23 @@
+package use_case.note.search_return;
+
+import entity.Weather;
+
+/**
+ * Output data for the search return use case.
+ */
+public class SearchReturnOutputData {
+
+ private final String location;
+ private final Weather weather;
+ private final boolean useCaseFailed;
+
+ public SearchReturnOutputData(Weather weather, boolean useCaseFailed) {
+ this.location = weather.getCityName();
+ this.weather = weather;
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public Weather getWeather() {
+ return weather;
+ }
+}
diff --git a/src/main/java/view/CompareCitiesView.java b/src/main/java/view/CompareCitiesView.java
new file mode 100644
index 000000000..e83b48349
--- /dev/null
+++ b/src/main/java/view/CompareCitiesView.java
@@ -0,0 +1,140 @@
+package view;
+
+import interface_adapter.CompareCities.CompareCitiesState;
+import interface_adapter.CompareCities.CompareCitiesViewModel;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class CompareCitiesView extends JFrame {
+ private final LabelTextPanel weatherincitypanelA;
+ private final LabelTextPanel temperaturepanelA;
+ private LabelTextPanel skyconditionpanelA;
+ private LabelTextPanel humiditypanelA;
+ private LabelTextPanel windspeedpanelA;
+ private LabelTextPanel visibilitypanelA;
+
+ private final JLabel cityA = new JLabel("");
+ private final JLabel tempA = new JLabel("");
+ private final JLabel skyconditionA = new JLabel("");
+ private final JLabel humidityA = new JLabel("");
+ private final JLabel windspeedA = new JLabel("");
+ private final JLabel visibilityA = new JLabel("");
+
+ private final LabelTextPanel weatherincitypanelB;
+ private final LabelTextPanel temperaturepanelB;
+ private LabelTextPanel skyconditionpanelB;
+ private LabelTextPanel humiditypanelB;
+ private LabelTextPanel windspeedpanelB;
+ private LabelTextPanel visibilitypanelB;
+
+ private LabelTextPanel travelcityA;
+ private LabelTextPanel travelcityB;
+
+ private final JLabel cityB = new JLabel("");
+ private final JLabel tempB = new JLabel("");
+ private final JLabel skyconditionB = new JLabel("");
+ private final JLabel humidityB = new JLabel("");
+ private final JLabel windspeedB = new JLabel("");
+ private final JLabel visibilityB = new JLabel("");
+
+ private final int frameheight = 600;
+ private final int framewidth = 1000;
+ private JLabel reasonA;
+ private JLabel reasonB;
+
+ private CompareCitiesViewModel viewModel;
+
+ public CompareCitiesView(CompareCitiesViewModel viewModel) {
+ this.viewModel = viewModel;
+// viewModel.addPropertyChangeListener(this);
+ final CompareCitiesState compareCitiesState = this.viewModel.getState();
+ cityA.setText(compareCitiesState.getFirstWeather().getCityName());
+ tempA.setText(String.valueOf(compareCitiesState.getFirstWeather().getTemperature()));
+ skyconditionA.setText(compareCitiesState.getFirstWeather().getWeather());
+ humidityA.setText(String.valueOf(compareCitiesState.getFirstWeather().getHumidity()));
+ windspeedA.setText(String.valueOf(compareCitiesState.getFirstWeather().getWindSpeed()));
+ visibilityA.setText(String.valueOf(compareCitiesState.getFirstWeather().getVisibility()));
+ cityB.setText(compareCitiesState.getSecondWeather().getCityName());
+ tempB.setText(String.valueOf(compareCitiesState.getSecondWeather().getTemperature()));
+ skyconditionB.setText(compareCitiesState.getSecondWeather().getWeather());
+ humidityB.setText(String.valueOf(compareCitiesState.getSecondWeather().getHumidity()));
+ windspeedB.setText(String.valueOf(compareCitiesState.getSecondWeather().getWindSpeed()));
+ visibilityB.setText(String.valueOf(compareCitiesState.getSecondWeather().getVisibility()));
+
+ weatherincitypanelA = new LabelTextPanel(new JLabel("Current Weather in"), cityA);
+ temperaturepanelA = new LabelTextPanel(new JLabel("Temperature"), tempA);
+ skyconditionpanelA = new LabelTextPanel(new JLabel("Sky"), skyconditionA);
+ humiditypanelA = new LabelTextPanel(new JLabel("Humidity"), humidityA);
+ windspeedpanelA = new LabelTextPanel(new JLabel("Wind"), windspeedA);
+ visibilitypanelA = new LabelTextPanel(new JLabel("Visibility"), visibilityA);
+
+ weatherincitypanelB = new LabelTextPanel(new JLabel("Current Weather in"), cityB);
+ temperaturepanelB = new LabelTextPanel(new JLabel("Temperature"), tempB);
+ skyconditionpanelB = new LabelTextPanel(new JLabel("Sky"), skyconditionB);
+ humiditypanelB = new LabelTextPanel(new JLabel("Humidity"), humidityB);
+ windspeedpanelB = new LabelTextPanel(new JLabel("Wind"), windspeedB);
+ visibilitypanelB = new LabelTextPanel(new JLabel("Visibility"), visibilityB);
+
+ if (Math.max(Double.parseDouble(tempA.getText()),
+ Double.parseDouble(tempB.getText())) == Double.parseDouble(tempA.getText())) {
+ reasonA = new JLabel("Warmer Temperature \uD83D\uDD25");
+ }
+ else {
+ reasonA = new JLabel("Cooler Temperature ⛄");
+ }
+ travelcityA = new LabelTextPanel(new JLabel("Travel to this city if you like: "), reasonA);
+
+ if ("Clouds".equals(skyconditionB.getText())) {
+ reasonB = new JLabel("Clouds ☁");
+ }
+ else if ("Clear".equals(skyconditionB.getText())){
+ reasonB = new JLabel("Sunshine ☀");
+ }
+ else {
+ reasonB = new JLabel("Rainy \uD83C\uDF27\uFE0F");
+ }
+
+ travelcityB = new LabelTextPanel(new JLabel("Travel to this city if you like: "), reasonB);
+
+ this.setLayout(new GridLayout(7, 2));
+ this.add(weatherincitypanelA);
+ this.add(weatherincitypanelB);
+ this.add(temperaturepanelA);
+ this.add(temperaturepanelB);
+ this.add(skyconditionpanelA);
+ this.add(skyconditionpanelB);
+ this.add(humiditypanelA);
+ this.add(humiditypanelB);
+ this.add(windspeedpanelA);
+ this.add(windspeedpanelB);
+ this.add(visibilitypanelA);
+ this.add(visibilitypanelB);
+ this.add(travelcityA);
+ this.add(travelcityB);
+ this.setSize(framewidth, frameheight);
+ this.setVisible(true);
+ this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ }
+
+// public void propertyChange(PropertyChangeEvent evt) {
+// final CompareCitiesState compareCitiesState = (CompareCitiesState) evt.getNewValue();
+// setfield(compareCitiesState);
+// }
+//
+// public void setfield(CompareCitiesState compareCitiesState) {
+// final CompareCitiesState compareCitiesState = this.viewModel.getState();
+// cityA.setText(compareCitiesState.getFirstWeather().getCityName());
+// tempA.setText(String.valueOf(compareCitiesState.getFirstWeather().getTemperature()));
+// skyconditionA.setText(compareCitiesState.getFirstWeather().getWeather());
+// humidityA.setText(String.valueOf(compareCitiesState.getFirstWeather().getHumidity()));
+// windspeedA.setText(String.valueOf(compareCitiesState.getFirstWeather().getWindSpeed()));
+// visibilityA.setText(String.valueOf(compareCitiesState.getFirstWeather().getVisibility()));
+// cityB.setText(compareCitiesState.getSecondWeather().getCityName());
+// tempB.setText(String.valueOf(compareCitiesState.getSecondWeather().getTemperature()));
+// skyconditionB.setText(compareCitiesState.getSecondWeather().getWeather());
+// humidityB.setText(String.valueOf(compareCitiesState.getSecondWeather().getHumidity()));
+// windspeedB.setText(String.valueOf(compareCitiesState.getSecondWeather().getWindSpeed()));
+// visibilityB.setText(String.valueOf(compareCitiesState.getSecondWeather().getVisibility()));
+// }
+}
diff --git a/src/main/java/view/HistoricalSearchedWeatherView.java b/src/main/java/view/HistoricalSearchedWeatherView.java
new file mode 100644
index 000000000..9fe85699f
--- /dev/null
+++ b/src/main/java/view/HistoricalSearchedWeatherView.java
@@ -0,0 +1,81 @@
+package view;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+
+import interface_adapter.SearchResult.SearchResultState;
+import interface_adapter.SearchResult.SearchResultViewModel;
+
+/**
+ * The View for when the user is viewing a note in the program.
+ */
+public class HistoricalSearchedWeatherView extends JPanel implements PropertyChangeListener {
+
+ private static final int HISTORICALPANELWIDTH = 370;
+ private static final int HISTORICALPANELHEIGHT = 400;
+ private final SearchResultViewModel searchResultViewModel;
+ private LabelTextPanel weatherfincitypanel;
+ private LabelTextPanel temperaturepanel;
+ private LabelTextPanel skyconditionpanel;
+ private LabelTextPanel humiditypanel;
+ private LabelTextPanel windspeedpanel;
+ private LabelTextPanel visibilitypanel;
+ private LabelTextPanel citypanel;
+
+ private final JLabel emptylabel = new JLabel("");
+
+ private final JLabel noteName = new JLabel("search result");
+ private final JTextArea cityInputField = new JTextArea();
+ private final JTextArea dateInputField = new JTextArea();
+ private final JLabel city = new JLabel("");
+ private final JLabel temp = new JLabel("");
+ private final JLabel skycondition = new JLabel("");
+ private final JLabel humidity = new JLabel("");
+ private final JLabel windspeed = new JLabel("");
+ private final JLabel visibility = new JLabel("");
+
+ public HistoricalSearchedWeatherView(SearchResultViewModel searchResultViewModel, PropertyChangeEvent evt) {
+ this.searchResultViewModel = searchResultViewModel;
+ // Users can search for weather at a given time
+ this.searchResultViewModel.addPropertyChangeListener(this);
+
+ this.setSize(HISTORICALPANELWIDTH, HISTORICALPANELHEIGHT);
+ weatherfincitypanel = new LabelTextPanel(new JLabel("Historic Weather in: "), city);
+ temperaturepanel = new LabelTextPanel(new JLabel("Temperature: "), temp);
+ // Note we want to add a convertor here.The button needs an action listener.
+ skyconditionpanel = new LabelTextPanel(new JLabel("Sky: "), skycondition);
+ humiditypanel = new LabelTextPanel(new JLabel("Humidity: "), humidity);
+ windspeedpanel = new LabelTextPanel(new JLabel("Wind: "), windspeed);
+ visibilitypanel = new LabelTextPanel(new JLabel("Visibility: "), visibility);
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ this.add(weatherfincitypanel);
+ this.add(temperaturepanel);
+ this.add(skyconditionpanel);
+ this.add(humiditypanel);
+ this.add(windspeedpanel);
+ this.add(visibilitypanel);
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ final String propertyName = evt.getPropertyName();
+ final String search = "searchResult";
+ if (propertyName.equals(search)) {
+ final SearchResultState state = (SearchResultState) evt.getNewValue();
+
+ city.setText(state.getWeather().getCityName());
+ temp.setText(state.getWeather().getTemperature() + "°C");
+ skycondition.setText(state.getWeather().getWeather());
+ humidity.setText(Math.abs(state.getWeather().getHumidity()) + "%");
+ windspeed.setText(state.getWeather().getWindSpeed() + "m/s");
+ visibility.setText(state.getWeather().getVisibility() + "m");
+
+ }
+
+ }
+}
diff --git a/src/main/java/view/LabelTextPanel.java b/src/main/java/view/LabelTextPanel.java
new file mode 100644
index 000000000..00b67c912
--- /dev/null
+++ b/src/main/java/view/LabelTextPanel.java
@@ -0,0 +1,30 @@
+package view;
+
+import javax.swing.*;
+
+/**
+ * A panel containing a label and a text field.
+ */
+public class LabelTextPanel extends JPanel {
+ private JLabel output;
+
+ public LabelTextPanel(JLabel label, JTextField textField) {
+ this.output = null;
+ this.add(label);
+ this.add(textField);
+ }
+
+ public LabelTextPanel(JLabel label, JLabel info) {
+ this.output = null;
+ this.add(label);
+ this.add(info);
+ }
+
+ public JLabel getoutput() {
+ return this.output;
+ }
+
+ public void setoutput(String outputstring) {
+ this.output = new JLabel(outputstring);
+ }
+}
diff --git a/src/main/java/view/MainView.java b/src/main/java/view/MainView.java
new file mode 100644
index 000000000..c600ae53f
--- /dev/null
+++ b/src/main/java/view/MainView.java
@@ -0,0 +1,44 @@
+package view;
+
+import interface_adapter.SearchResult.SearchResultViewModel;
+import interface_adapter.nearby_list.NearbyListViewModel;
+import interface_adapter.weather.WeatherViewModel;
+
+import javax.swing.JFrame;
+import java.awt.GridLayout;
+import java.beans.PropertyChangeEvent;
+
+public class MainView extends JFrame {
+ public NearbyCitiesView nearbyCitiesView;
+ public MapPanelView mapPanelView;
+ public WeatherPanelView weatherPanelView;
+ private HistoricalSearchedWeatherView historicalSearchedWeatherView;
+
+ private final int frameWidth = 1500;
+ private final int frameHeight = 1000;
+
+ public MainView(NearbyListViewModel nearbyListViewModel, WeatherViewModel weatherViewModel, SearchResultViewModel searchResultViewModel, PropertyChangeEvent evt) {
+ nearbyCitiesView = new NearbyCitiesView(nearbyListViewModel, weatherViewModel);
+ mapPanelView = new MapPanelView(weatherViewModel);
+ weatherPanelView = new WeatherPanelView(weatherViewModel, evt);
+ historicalSearchedWeatherView = new HistoricalSearchedWeatherView(searchResultViewModel, evt);
+ this.setTitle("Weather Wizard");
+ this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ this.setSize(frameWidth, frameHeight);
+ // I choose to use 1X2 gridlayout so we can have both panel side by side
+ this.setLayout(new GridLayout(1, 4));
+ this.add(nearbyCitiesView);
+ this.add(mapPanelView);
+ this.add(weatherPanelView);
+ this.add(historicalSearchedWeatherView);
+ // pack() optimize window size
+ this.pack();
+ this.setVisible(true);
+
+ }
+
+// public static void man(String[] args) {
+// new MainView();
+// }
+
+}
diff --git a/src/main/java/view/MapImage.png b/src/main/java/view/MapImage.png
new file mode 100644
index 000000000..699acf984
Binary files /dev/null and b/src/main/java/view/MapImage.png differ
diff --git a/src/main/java/view/MapImagepanel.java b/src/main/java/view/MapImagepanel.java
new file mode 100644
index 000000000..14bead688
--- /dev/null
+++ b/src/main/java/view/MapImagepanel.java
@@ -0,0 +1,65 @@
+package view;
+
+import interface_adapter.weather.WeatherState;
+import interface_adapter.weather.WeatherViewModel;
+import org.jxmapviewer.JXMapViewer;
+import org.jxmapviewer.OSMTileFactoryInfo;
+import org.jxmapviewer.viewer.DefaultTileFactory;
+import org.jxmapviewer.viewer.GeoPosition;
+import org.jxmapviewer.viewer.TileFactoryInfo;
+
+import javax.swing.*;
+import java.awt.*;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+
+/*
+ * the MapImagePanel is responsible for displaying the map file.
+ */
+public class MapImagepanel extends JPanel implements PropertyChangeListener {
+ private static final int ZOOM_VALUE = 13;
+ private static final int NUM_THREADS = 8;
+
+ private final WeatherViewModel weatherViewModel;
+ private GeoPosition position;
+ private JXMapViewer mapViewer;
+
+ // Generates a JXMapViewer object given latitude and longitude
+ public MapImagepanel(WeatherViewModel weatherViewModel, double latitude, double longitude) {
+ this.weatherViewModel = weatherViewModel;
+ this.weatherViewModel.addPropertyChangeListener(this);
+
+ this.mapViewer = new JXMapViewer();
+
+ final TileFactoryInfo info = new OSMTileFactoryInfo();
+ final DefaultTileFactory tileFactory = new DefaultTileFactory(info);
+
+ this.mapViewer.setTileFactory(tileFactory);
+
+ tileFactory.setThreadPoolSize(NUM_THREADS);
+
+ this.position = new GeoPosition(latitude, longitude);
+
+ this.mapViewer.setZoom(ZOOM_VALUE);
+ mapViewer.setAddressLocation(position);
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ final WeatherState weatherState = (WeatherState) evt.getNewValue();
+ setPosition(weatherState.getWeather().getLat(), weatherState.getWeather().getLon());
+
+ }
+
+ private void setPosition(double latitude, double longitude) {
+ this.position = new GeoPosition(latitude, longitude);
+ this.mapViewer.setAddressLocation(position);
+ }
+
+ public JXMapViewer getDisplayfield() {
+ return this.mapViewer;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/view/MapPanelView.java b/src/main/java/view/MapPanelView.java
new file mode 100644
index 000000000..47467822c
--- /dev/null
+++ b/src/main/java/view/MapPanelView.java
@@ -0,0 +1,290 @@
+//package view;
+//
+//import app.MainApplication;
+//import interface_adapter.CompareCities.CompareCitiesController;
+//import interface_adapter.CompareCities.CompareCitiesState;
+//import interface_adapter.CompareCities.CompareCitiesViewModel;
+//import interface_adapter.SearchResult.SearchResultController;
+//import interface_adapter.alert_pop.AlertPopController;
+//import interface_adapter.converter.ConverterController;
+//import interface_adapter.nearby_list.NearbyListController;
+//import interface_adapter.weather.WeatherController;
+//import interface_adapter.weather.WeatherViewModel;
+//
+//import javax.swing.*;
+//import java.awt.event.ActionEvent;
+//import java.awt.event.ActionListener;
+//import java.beans.PropertyChangeEvent;
+//
+///*
+//* This class responsible for creating the Map Subpanel of the main. The Map subpanel itself contains 4 parts:
+//* 1. city input panel where user can type the city name. This is connected to an Action Lisenter, which pass infor
+//* to our weatherContoller Class.
+//* 2. date input panel
+//* 4. compare to button
+//* 3. mapimagepanel.getDisplayfield where we display the image of the map using Jlabel format.
+// */
+//@SuppressWarnings("checkstyle:WriteTag")
+//public class MapPanelView extends JPanel implements ActionListener {
+// private final LabelTextPanel cityinputpanel;
+// private final LabelTextPanel dateinputpanel;
+// private final LabelTextPanel comparetopanel;
+// private final MapImagepanel mapimagepanel;
+//
+// private final JTextField cityinputfield1 = new JTextField(20);
+// private final JTextField dateinputfield = new JTextField(20);
+// private final JTextField cityinputfield2 = new JTextField(20);
+// private final int mappanelwidth = 370;
+// private final int mappanelheight = 500;
+//
+// private SearchResultController searchResultController;
+// private WeatherController weatherController;
+// private CompareCitiesController compareCitiesController;
+// private NearbyListController nearbyListController;
+// private AlertPopController alertPopController;
+// private final double torontoLatitude = 43.6532;
+// private final double torontoLongitude = -79.3832;
+//
+// private CompareCitiesViewModel compareCitiesViewModel;
+//
+// public MapPanelView(WeatherViewModel weatherViewModel) {
+// // by default set the map center be Toronto.
+// mapimagepanel = new MapImagepanel(weatherViewModel, torontoLatitude, torontoLongitude);
+// // when we get one city name -> weather contoller
+// cityinputfield1.addActionListener(
+// event -> {
+// // if the event is coming from cityinput field, execute weather controller, check if empty
+// if (event.getSource() == cityinputfield1 && cityinputfield1.getText().length() > 0) {
+// weatherController.execute(cityinputfield1.getText());
+// }
+// else {
+// cityinputfield1.setText("can not return empty");
+// }
+// }
+// );
+// // if Compare to another city -> CompareCityController
+// cityinputpanel = new LabelTextPanel(new JLabel("search city"), cityinputfield1);
+// dateinputpanel = new LabelTextPanel(new JLabel("date"), dateinputfield);
+// comparetopanel = new LabelTextPanel(new JLabel("Compare To"), cityinputfield2);
+//
+// dateinputfield.addActionListener(
+// // if this event is coming from dateinput field, execute searchresult contoller
+// event -> {
+// if (event.getSource() == dateinputfield) {
+// searchResultController.execute(cityinputfield1.getText(), dateinputfield.getText());
+// }
+// });
+//// this.setSize(mappanelwidth, mappanelheight);
+// this.setPreferredSize(new java.awt.Dimension(mappanelwidth, mappanelheight));
+// this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+// this.add(cityinputpanel);
+// this.add(dateinputpanel);
+// this.add(comparetopanel);
+// // adding a Jlabel
+// this.add(mapimagepanel.getDisplayfield());
+//
+// }
+//
+// @Override
+// /*
+// * prints a message to the console when an action event occurs. Will look something like "Enter city name"
+// */
+// public void actionPerformed(ActionEvent event) {
+// System.out.println("Enter" + event.getActionCommand());
+//
+// }
+//
+// public void setCompareCitiesViewModel(CompareCitiesViewModel compareCitiesViewModel) {
+// this.compareCitiesViewModel = compareCitiesViewModel;
+// }
+//// public void propertyChange(PropertyChangeEvent evt) {
+//// final WeatherState state = (WeatherState) evt.getNewValue();
+//// setFields(state);
+//// if (state.getError() != null) {
+//// JOptionPane.showMessageDialog(this, state.getError(),
+//// "Error", JOptionPane.ERROR_MESSAGE);
+//// }
+//// }
+////
+//// private void setFields(WeatherState state) {
+//// cityinputfield.setText(state.getWeather());
+//// }
+//
+// public void setWeatherController(WeatherController weathercontroller) {
+// this.weatherController = weathercontroller;
+// }
+//
+// public void setSearchResultController(SearchResultController searchresultcontroller) {
+// this.searchResultController = searchresultcontroller;
+// }
+//
+// public void setNearbyListController(NearbyListController nearbyListController) {
+// this.nearbyListController = nearbyListController;
+// }
+//
+// public void setAlertPopController(AlertPopController alertPopController) {
+// this.alertPopController = alertPopController;
+// }
+//
+// public void setCompareCitiesController(CompareCitiesController compareCitiesController) {
+// this.compareCitiesController = compareCitiesController;
+// }
+//}
+
+package view;
+
+import interface_adapter.CompareCities.CompareCitiesController;
+import interface_adapter.CompareCities.CompareCitiesViewModel;
+import interface_adapter.SearchResult.SearchResultController;
+import interface_adapter.alert_pop.AlertPopController;
+import interface_adapter.converter.ConverterController;
+import interface_adapter.nearby_list.NearbyListController;
+import interface_adapter.weather.WeatherController;
+import interface_adapter.weather.WeatherViewModel;
+
+import javax.swing.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+
+/*
+ * This class responsible for creating the Map Subpanel of the main. The Map subpanel itself contains 4 parts:
+ * 1. city input panel where user can type the city name. This is connected to an Action Lisenter, which pass infor
+ * to our weatherContoller Class.
+ * 2. date input panel
+ * 4. compare to button
+ * 3. mapimagepanel.getDisplayfield where we display the image of the map using Jlabel format.
+ */
+@SuppressWarnings("checkstyle:WriteTag")
+public class MapPanelView extends JPanel implements ActionListener {
+ private final LabelTextPanel cityinputpanel;
+ private final LabelTextPanel dateinputpanel;
+ private final LabelTextPanel comparetopanel;
+ private final MapImagepanel mapimagepanel;
+ private LabelTextPanel timepanel;
+
+ private final JLabel time = new JLabel("");
+
+ private final JTextField cityinputfield1 = new JTextField(20);
+ private final JTextField dateinputfield = new JTextField(20);
+ private final JTextField cityinputfield2 = new JTextField(20);
+ private final int mappanelwidth = 370;
+ private final int mappanelheight = 500;
+
+ private SearchResultController searchResultController;
+ private WeatherController weatherController;
+ private CompareCitiesController compareCitiesController;
+ private NearbyListController nearbyListController;
+ private AlertPopController alertPopController;
+ private final double torontoLatitude = 43.70011;
+ private final double torontoLongitude = -79.4163;
+ private CompareCitiesViewModel compareCitiesViewModel;
+
+ public MapPanelView(WeatherViewModel weatherViewModel) {
+ // by default set the map center be Toronto.
+
+ timepanel = new LabelTextPanel(new JLabel("Time"), time);
+ mapimagepanel = new MapImagepanel(weatherViewModel, torontoLatitude, torontoLongitude);
+ // when we get one city name -> weather contoller
+ cityinputfield1.addActionListener(
+ event -> {
+ // if the event is coming from cityinput field, execute weather controller, check if empty
+ if (event.getSource() == cityinputfield1 && cityinputfield1.getText().length() > 0) {
+ weatherController.execute(capitalizeCity(cityinputfield1.getText()));
+ cityinputfield1.setText("");
+ final DateTimeFormatter formatter = DateTimeFormatter
+ .ofPattern("yyyy-MM-dd HH").withZone(ZoneOffset.UTC);
+ final String timestamp = formatter.format(Instant.now());
+ time.setText(timestamp);
+ }
+ else {
+ cityinputfield1.setText("can not return empty");
+ }
+ }
+ );
+ // if Compare to another city -> CompareCityController
+ cityinputfield2.addActionListener(
+ event -> {
+ if (cityinputfield1.getText().length() > 0 && cityinputfield2.getText().length() > 0) {
+
+ // some how the view model doesn't get update
+ compareCitiesController.execute(capitalizeCity(cityinputfield1.getText()), capitalizeCity(cityinputfield2.getText()));
+
+ new CompareCitiesView(this.compareCitiesViewModel);
+ }
+ else {
+ cityinputfield2.setText("can not return empty");
+ }
+ }
+ );
+ cityinputpanel = new LabelTextPanel(new JLabel("City Name"), cityinputfield1);
+ dateinputpanel = new LabelTextPanel(new JLabel("Time (YYYY-MM-DD hh)"), dateinputfield);
+ comparetopanel = new LabelTextPanel(new JLabel("Compare To"), cityinputfield2);
+ timepanel = new LabelTextPanel(new JLabel("Current Time"), time);
+
+ dateinputfield.addActionListener(
+ // if this event is coming from dateinput field, execute searchresult contoller
+ event -> {
+ if (event.getSource() == dateinputfield) {
+ searchResultController
+ .execute(capitalizeCity(cityinputfield1.getText()), dateinputfield.getText());
+ cityinputfield1.setText("");
+ dateinputfield.setText("");
+ }
+ });
+ this.setPreferredSize(new java.awt.Dimension(mappanelwidth, mappanelheight));
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ this.add(cityinputpanel);
+ this.add(dateinputpanel);
+ this.add(comparetopanel);
+ this.add(timepanel);
+ // adding a Jlabel
+ this.add(mapimagepanel.getDisplayfield());
+
+ }
+
+ private String capitalizeCity(String cityName) {
+ final String[] split = cityName.split(" ");
+ final StringBuilder output = new StringBuilder();
+ for (String part : split) {
+ output.append(part.substring(0, 1).toUpperCase())
+ .append(part.substring(1).toLowerCase()).append(" ");
+ }
+ return output.toString().trim();
+ }
+
+ @Override
+ /*
+ * prints a message to the console when an action event occurs. Will look something like "Enter city name"
+ */
+ public void actionPerformed(ActionEvent event) {
+ System.out.println("Enter" + event.getActionCommand());
+ }
+
+ public void setWeatherController(WeatherController weathercontroller) {
+ this.weatherController = weathercontroller;
+ }
+
+ public void setSearchResultController(SearchResultController searchresultcontroller) {
+ this.searchResultController = searchresultcontroller;
+ }
+
+ public void setNearbyListController(NearbyListController nearbyListController) {
+ this.nearbyListController = nearbyListController;
+ }
+
+ public void setAlertPopController(AlertPopController alertPopController) {
+ this.alertPopController = alertPopController;
+ }
+
+ public void setCompareCitiesController(CompareCitiesController compareCitiesController) {
+ this.compareCitiesController = compareCitiesController;
+ }
+
+ public void setCompareCitiesViewModel(CompareCitiesViewModel compareCitiesViewModel){
+ this.compareCitiesViewModel = compareCitiesViewModel;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/view/NearbyCitiesView.java b/src/main/java/view/NearbyCitiesView.java
new file mode 100644
index 000000000..f9c91d5ec
--- /dev/null
+++ b/src/main/java/view/NearbyCitiesView.java
@@ -0,0 +1,69 @@
+package view;
+
+import interface_adapter.nearby_list.NearbyListController;
+import interface_adapter.nearby_list.NearbyListState;
+import interface_adapter.nearby_list.NearbyListViewModel;
+import interface_adapter.weather.WeatherController;
+import interface_adapter.weather.WeatherState;
+import interface_adapter.weather.WeatherViewModel;
+
+import javax.swing.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+public class NearbyCitiesView extends JPanel implements PropertyChangeListener {
+ private final JLabel listInputPanel;
+ private final JList cities;
+ private final NearbyListViewModel nearbyListViewModel;
+ private final WeatherViewModel weatherViewModel;
+
+ private NearbyListController nearbyListController;
+ private WeatherController weatherController;
+
+ public NearbyCitiesView(NearbyListViewModel nearbyListViewModel, WeatherViewModel weatherViewModel) {
+ this.nearbyListViewModel = nearbyListViewModel;
+ this.weatherViewModel = weatherViewModel;
+ this.nearbyListViewModel.addPropertyChangeListener(this);
+ this.weatherViewModel.addPropertyChangeListener(this);
+
+ this.listInputPanel = new JLabel("Nearby cities (double click to select):");
+
+ this.add(this.listInputPanel);
+
+ cities = new JList<>();
+
+ cities.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent evt) {
+ if (evt.getClickCount() == 2) {
+ weatherController.execute(cities.getSelectedValue());
+ }
+ }
+ });
+
+ this.add(cities);
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals("Weather")) {
+ final WeatherState weatherState = (WeatherState) evt.getNewValue();
+ nearbyListController.execute(weatherState.getWeather().getLon(), weatherState.getWeather().getLat());
+ }
+ else if (evt.getPropertyName().equals("NearbyList")) {
+ final NearbyListState nearbyListState = (NearbyListState) evt.getNewValue();
+ cities.setListData(nearbyListState.getCities());
+ }
+ }
+
+ public void setNearbyListController(NearbyListController nearbyListController) {
+ this.nearbyListController = nearbyListController;
+ }
+
+ public void setWeatherController(WeatherController weatherController) {
+ this.weatherController = weatherController;
+ }
+
+}
diff --git a/src/main/java/view/NoteView.java b/src/main/java/view/NoteView.java
deleted file mode 100644
index 331d76493..000000000
--- a/src/main/java/view/NoteView.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package view;
-
-import java.awt.Component;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JTextArea;
-
-import interface_adapter.note.NoteController;
-import interface_adapter.note.NoteState;
-import interface_adapter.note.NoteViewModel;
-
-/**
- * The View for when the user is viewing a note in the program.
- */
-public class NoteView extends JPanel implements ActionListener, PropertyChangeListener {
-
- private final NoteViewModel noteViewModel;
-
- private final JLabel noteName = new JLabel("note for jonathan_calver2");
- private final JTextArea noteInputField = new JTextArea();
-
- private final JButton saveButton = new JButton("Save");
- private final JButton refreshButton = new JButton("Refresh");
- private NoteController noteController;
-
- public NoteView(NoteViewModel noteViewModel) {
-
- noteName.setAlignmentX(Component.CENTER_ALIGNMENT);
- this.noteViewModel = noteViewModel;
- this.noteViewModel.addPropertyChangeListener(this);
-
- final JPanel buttons = new JPanel();
- buttons.add(saveButton);
- buttons.add(refreshButton);
-
- saveButton.addActionListener(
- evt -> {
- if (evt.getSource().equals(saveButton)) {
- noteController.execute(noteInputField.getText());
-
- }
- }
- );
-
- refreshButton.addActionListener(
- evt -> {
- if (evt.getSource().equals(refreshButton)) {
- noteController.execute(null);
-
- }
- }
- );
-
- this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
-
- this.add(noteName);
- this.add(noteInputField);
- this.add(buttons);
- }
-
- /**
- * React to a button click that results in evt.
- * @param evt the ActionEvent to react to
- */
- public void actionPerformed(ActionEvent evt) {
- System.out.println("Click " + evt.getActionCommand());
- }
-
- @Override
- public void propertyChange(PropertyChangeEvent evt) {
- final NoteState state = (NoteState) evt.getNewValue();
- setFields(state);
- if (state.getError() != null) {
- JOptionPane.showMessageDialog(this, state.getError(),
- "Error", JOptionPane.ERROR_MESSAGE);
- }
- }
-
- private void setFields(NoteState state) {
- noteInputField.setText(state.getNote());
- }
-
- public void setNoteController(NoteController controller) {
- this.noteController = controller;
- }
-}
-
diff --git a/src/main/java/view/WeatherPanelView.java b/src/main/java/view/WeatherPanelView.java
new file mode 100644
index 000000000..89c63c739
--- /dev/null
+++ b/src/main/java/view/WeatherPanelView.java
@@ -0,0 +1,142 @@
+package view;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import data_access.WeatherDataAccessObject;
+import entity.Weather;
+import interface_adapter.SearchResult.SearchResultViewModel;
+import interface_adapter.converter.ConverterController;
+//import interface_adapter.weather.WeatherController;
+import interface_adapter.weather.WeatherState;
+import interface_adapter.weather.WeatherViewModel;
+
+/**
+ * This class responsible for creating the Weather Subpanel of the main. The Weather subpanel itself contains a bunch
+ * of LabelTextPanel that displays varies weather information.
+ * This part of view will have to change based on the output, so it depends on the view model
+ **/
+public class WeatherPanelView extends JPanel implements PropertyChangeListener, ActionListener {
+ private final WeatherViewModel weatherViewModel;
+ private SearchResultViewModel searchResultViewModel;
+
+ private final LabelTextPanel weatherincitypanel;
+ private final LabelTextPanel temperaturepanel;
+ private LabelTextPanel skyconditionpanel;
+ private LabelTextPanel humiditypanel;
+ private LabelTextPanel windspeedpanel;
+ private LabelTextPanel visibilitypanel;
+ private LabelTextPanel timepanel;
+ private LabelTextPanel unitpanel;
+ private Weather currentWeather;
+
+ private final JLabel metric = new JLabel("Metric");
+ private final JLabel city = new JLabel("");
+ private final JLabel temp = new JLabel("");
+ private final JLabel skycondition = new JLabel("");
+ private final JLabel humidity = new JLabel("");
+ private final JLabel windspeed = new JLabel("");
+ private final JLabel visibility = new JLabel("");
+ private final JLabel time = new JLabel("");
+ private final JButton unitconverter;
+
+ private ConverterController convertorController;
+ private static final int WEATHER_PANEL_WIDTH = 370;
+ private static final int WEATHERPANELHEIGHT = 400;
+
+ public WeatherPanelView(WeatherViewModel weatherViewModel, PropertyChangeEvent evt) {
+ this.weatherViewModel = weatherViewModel;
+ this.weatherViewModel.addPropertyChangeListener(this);
+
+ this.setSize(WEATHER_PANEL_WIDTH, WEATHERPANELHEIGHT);
+ weatherincitypanel = new LabelTextPanel(new JLabel("Current Weather in: "), city);
+ temperaturepanel = new LabelTextPanel(new JLabel("Temperature: "), temp);
+ timepanel = new LabelTextPanel(new JLabel("Time: "), time);
+ // Note we want to add a convertor that convert the weather information from degree celsius to fahrenheit,
+ // or the opposite.The button needs an action listener that pass the change to a ConverterController.
+ this.unitconverter = new JButton("Unit Converter");
+ unitconverter.addActionListener(event -> {
+ // if the event is coming from temperature converter button, execute convertor controller
+ if (event.getSource() == unitconverter) {
+
+ if (city != null) {
+ final Weather tempWeather = currentWeather;
+ convertorController.execute(tempWeather);
+ System.out.println("worked");
+ System.out.println(tempWeather.getCityName());
+ }
+
+ }
+ });
+
+ unitpanel = new LabelTextPanel(new JLabel("Unit: "), metric);
+ skyconditionpanel = new LabelTextPanel(new JLabel("Sky: "), skycondition);
+ humiditypanel = new LabelTextPanel(new JLabel("Humidity: "), humidity);
+ windspeedpanel = new LabelTextPanel(new JLabel("Wind: "), windspeed);
+ visibilitypanel = new LabelTextPanel(new JLabel("Visibility: "), visibility);
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ this.add(weatherincitypanel);
+ this.add(temperaturepanel);
+ this.add(skyconditionpanel);
+ this.add(humiditypanel);
+ this.add(windspeedpanel);
+ this.add(visibilitypanel);
+ this.add(unitconverter);
+ this.add(unitpanel);
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ final WeatherState weatherState = (WeatherState) evt.getNewValue();
+ setfield(weatherState);
+ this.currentWeather = weatherState.getWeather();
+ System.out.println(weatherState.getWeather());
+ }
+
+ public void setfield(WeatherState weatherState) {
+ final boolean metric1 = weatherState.getWeather().isMetric();
+ if (metric1) {
+ metric.setText("Metric");
+ }
+ else {
+ metric.setText("Imperial");
+ }
+
+ city.setText(weatherState.getWeather().getCityName());
+ skycondition.setText(weatherState.getWeather().getWeather());
+ humidity.setText(weatherState.getWeather().getHumidity() + "%");
+ visibility.setText(weatherState.getWeather().getVisibility() + "m");
+ if (weatherState.getWeather().isMetric()) {
+ temp.setText(weatherState.getWeather().getTemperature() + "°C");
+ windspeed.setText(weatherState.getWeather().getWindSpeed() + "m/s");
+ }
+ else {
+ temp.setText(weatherState.getWeather().getTemperature() + "°F");
+ windspeed.setText(weatherState.getWeather().getWindSpeed() + "mph");
+ }
+ final DateTimeFormatter formatter = DateTimeFormatter
+ .ofPattern("yyyy-MM-dd HH").withZone(ZoneOffset.UTC);
+ final String timestamp = formatter.format(Instant.now());
+ time.setText(timestamp);
+ }
+
+ public void actionPerformed(ActionEvent event) {
+ System.out.println("Enter" + event.getActionCommand());
+
+ }
+
+ public void setConverterController(ConverterController converterController) {
+ this.convertorController = converterController;
+ }
+}
diff --git a/src/main/resources/cities_list.json b/src/main/resources/cities_list.json
new file mode 100644
index 000000000..391b7cb98
--- /dev/null
+++ b/src/main/resources/cities_list.json
@@ -0,0 +1,2228 @@
+[
+ {
+ "name": "Abidjan",
+ "lon": -4.00167,
+ "lat": 5.35444,
+ "country": "Ivory Coast"
+ },
+ {
+ "name": "Abu Dhabi",
+ "lon": 54.39696,
+ "lat": 24.45118,
+ "country": "United Arab Emirates"
+ },
+ {
+ "name": "Abuja",
+ "lon": 7.49508,
+ "lat": 9.05785,
+ "country": "Nigeria"
+ },
+ {
+ "name": "Accra",
+ "lon": -0.1969,
+ "lat": 5.55602,
+ "country": "Ghana"
+ },
+ {
+ "name": "Addis Ababa",
+ "lon": 38.74689,
+ "lat": 9.02497,
+ "country": "Ethiopia"
+ },
+ {
+ "name": "Ahmedabad",
+ "lon": 72.58727,
+ "lat": 23.02579,
+ "country": "India"
+ },
+ {
+ "name": "Aleppo",
+ "lon": 37.16117,
+ "lat": 36.20124,
+ "country": "Syria"
+ },
+ {
+ "name": "Alexandria",
+ "lon": 29.91582,
+ "lat": 31.20176,
+ "country": "Egypt"
+ },
+ {
+ "name": "Algiers",
+ "lon": 3.08746,
+ "lat": 36.73225,
+ "country": "Algeria"
+ },
+ {
+ "name": "Almaty",
+ "lon": 76.92861,
+ "lat": 43.25667,
+ "country": "Kazakhstan"
+ },
+ {
+ "name": "Amman",
+ "lon": 35.94503,
+ "lat": 31.95522,
+ "country": "Jordan"
+ },
+ {
+ "name": "Amsterdam",
+ "lon": 4.88969,
+ "lat": 52.37403,
+ "country": "Netherlands"
+ },
+ {
+ "name": "Anchorage",
+ "lon": -149.90028,
+ "lat": 61.21806,
+ "country": "United States"
+ },
+ {
+ "name": "Andorra la Vella",
+ "lon": 1.52109,
+ "lat": 42.50779,
+ "country": "Andorra"
+ },
+ {
+ "name": "Ankara",
+ "lon": 32.85427,
+ "lat": 39.91987,
+ "country": "Turkey"
+ },
+ {
+ "name": "Antananarivo",
+ "lon": 47.53613,
+ "lat": -18.91368,
+ "country": "Madagascar"
+ },
+ {
+ "name": "Apia",
+ "lon": -171.76666,
+ "lat": -13.83333,
+ "country": "Samoa"
+ },
+ {
+ "name": "Arnold",
+ "lon": -120.351935,
+ "lat": 38.255366,
+ "country": "United States"
+ },
+ {
+ "name": "Ashgabat",
+ "lon": 58.38333,
+ "lat": 37.95,
+ "country": "Turkmenistan"
+ },
+ {
+ "name": "Asmara",
+ "lon": 38.93184,
+ "lat": 15.33805,
+ "country": "Eritrea"
+ },
+ {
+ "name": "Asuncion",
+ "lon": -57.647,
+ "lat": -25.28646,
+ "country": "Paraguay"
+ },
+ {
+ "name": "Athens",
+ "lon": 23.72784,
+ "lat": 37.98376,
+ "country": "Greece"
+ },
+ {
+ "name": "Auckland",
+ "lon": 174.76349,
+ "lat": -36.84853,
+ "country": "New Zealand"
+ },
+ {
+ "name": "Avarua",
+ "lon": -159.77545,
+ "lat": -21.2075,
+ "country": "Cook Islands"
+ },
+ {
+ "name": "Baghdad",
+ "lon": 44.40088,
+ "lat": 33.34058,
+ "country": "Iraq"
+ },
+ {
+ "name": "Baku",
+ "lon": 49.89201,
+ "lat": 40.37767,
+ "country": "Azerbaijan"
+ },
+ {
+ "name": "Bamako",
+ "lon": -8,
+ "lat": 12.65,
+ "country": "Mali"
+ },
+ {
+ "name": "Banda Aceh",
+ "lon": 95.33333,
+ "lat": 5.54167,
+ "country": "Indonesia"
+ },
+ {
+ "name": "Bandar Seri Begawan",
+ "lon": 114.94006,
+ "lat": 4.89035,
+ "country": "Brunei"
+ },
+ {
+ "name": "Bandung",
+ "lon": 107.60694,
+ "lat": -6.92222,
+ "country": "Indonesia"
+ },
+ {
+ "name": "Bangkok",
+ "lon": 100.50144,
+ "lat": 13.75398,
+ "country": "Thailand"
+ },
+ {
+ "name": "Bangui",
+ "lon": 18.55496,
+ "lat": 4.36122,
+ "country": "Central African Republic"
+ },
+ {
+ "name": "Banjul",
+ "lon": -16.57803,
+ "lat": 13.45274,
+ "country": "Gambia"
+ },
+ {
+ "name": "Barcelona",
+ "lon": 2.15899,
+ "lat": 41.38879,
+ "country": "Spain"
+ },
+ {
+ "name": "Barranquilla",
+ "lon": -74.78132,
+ "lat": 10.96854,
+ "country": "Colombia"
+ },
+ {
+ "name": "Basrah",
+ "lon": 47.7804,
+ "lat": 30.50852,
+ "country": "Iraq"
+ },
+ {
+ "name": "Basse-Terre",
+ "lon": -61.73214,
+ "lat": 15.99714,
+ "country": "Guadeloupe"
+ },
+ {
+ "name": "Basseterre",
+ "lon": -62.72499,
+ "lat": 17.2955,
+ "country": "Saint Kitts and Nevis"
+ },
+ {
+ "name": "Beijing",
+ "lon": 116.39723,
+ "lat": 39.9075,
+ "country": "China"
+ },
+ {
+ "name": "Beirut",
+ "lon": 35.50157,
+ "lat": 33.89332,
+ "country": "Lebanon"
+ },
+ {
+ "name": "Bekasi",
+ "lon": 106.9896,
+ "lat": -6.2349,
+ "country": "Indonesia"
+ },
+ {
+ "name": "Belem",
+ "lon": -48.50444,
+ "lat": -1.45583,
+ "country": "Brazil"
+ },
+ {
+ "name": "Belgrade",
+ "lon": 20.46513,
+ "lat": 44.80401,
+ "country": "Serbia"
+ },
+ {
+ "name": "Belmopan",
+ "lon": -88.76667,
+ "lat": 17.25,
+ "country": "Belize"
+ },
+ {
+ "name": "Belo Horizonte",
+ "lon": -43.93778,
+ "lat": -19.92083,
+ "country": "Brazil"
+ },
+ {
+ "name": "Bengaluru",
+ "lon": 77.59369,
+ "lat": 12.97194,
+ "country": "India"
+ },
+ {
+ "name": "Berlin",
+ "lon": 13.41053,
+ "lat": 52.52437,
+ "country": "Germany"
+ },
+ {
+ "name": "Bern",
+ "lon": 7.44744,
+ "lat": 46.94809,
+ "country": "Switzerland"
+ },
+ {
+ "name": "Bishkek",
+ "lon": 74.59,
+ "lat": 42.87,
+ "country": "Kyrgyzstan"
+ },
+ {
+ "name": "Bissau",
+ "lon": -15.59767,
+ "lat": 11.86357,
+ "country": "Guinea-Bissau"
+ },
+ {
+ "name": "Bogota",
+ "lon": -74.08175,
+ "lat": 4.60971,
+ "country": "Colombia"
+ },
+ {
+ "name": "Brasilia",
+ "lon": -47.92972,
+ "lat": -15.77972,
+ "country": "Brazil"
+ },
+ {
+ "name": "Bratislava",
+ "lon": 17.10674,
+ "lat": 48.14816,
+ "country": "Slovakia"
+ },
+ {
+ "name": "Brazzaville",
+ "lon": 15.28318,
+ "lat": -4.26613,
+ "country": "Republic of the Congo"
+ },
+ {
+ "name": "Bridgetown",
+ "lon": -59.62021,
+ "lat": 13.10732,
+ "country": "Barbados"
+ },
+ {
+ "name": "Brisbane",
+ "lon": 153.02809,
+ "lat": -27.46794,
+ "country": "Australia"
+ },
+ {
+ "name": "Brussels",
+ "lon": 4.34878,
+ "lat": 50.85045,
+ "country": "Belgium"
+ },
+ {
+ "name": "Bucharest",
+ "lon": 26.10626,
+ "lat": 44.43225,
+ "country": "Romania"
+ },
+ {
+ "name": "Budapest",
+ "lon": 19.04045,
+ "lat": 47.49835,
+ "country": "Hungary"
+ },
+ {
+ "name": "Buenos Aires",
+ "lon": -58.37723,
+ "lat": -34.61315,
+ "country": "Argentina"
+ },
+ {
+ "name": "Bujumbura",
+ "lon": 29.36142,
+ "lat": -3.38193,
+ "country": "Burundi"
+ },
+ {
+ "name": "Bursa",
+ "lon": 29.06013,
+ "lat": 40.19559,
+ "country": "Turkey"
+ },
+ {
+ "name": "Busan",
+ "lon": 129.03004,
+ "lat": 35.10168,
+ "country": "South Korea"
+ },
+ {
+ "name": "Cairo",
+ "lon": 31.24967,
+ "lat": 30.06263,
+ "country": "Egypt"
+ },
+ {
+ "name": "Cali",
+ "lon": -76.5225,
+ "lat": 3.43722,
+ "country": "Colombia"
+ },
+ {
+ "name": "Caloocan",
+ "lon": 120.96788,
+ "lat": 14.64953,
+ "country": "Philippines"
+ },
+ {
+ "name": "Camayenne",
+ "lon": -13.68778,
+ "lat": 9.535,
+ "country": "Guinea"
+ },
+ {
+ "name": "Canberra",
+ "lon": 149.12807,
+ "lat": -35.28346,
+ "country": "Australia"
+ },
+ {
+ "name": "Cape Town",
+ "lon": 18.42322,
+ "lat": -33.92584,
+ "country": "South Africa"
+ },
+ {
+ "name": "Caracas",
+ "lon": -66.87919,
+ "lat": 10.48801,
+ "country": "Venezuela"
+ },
+ {
+ "name": "Casablanca",
+ "lon": -7.61138,
+ "lat": 33.58831,
+ "country": "Morocco"
+ },
+ {
+ "name": "Castries",
+ "lon": -61.00614,
+ "lat": 13.9957,
+ "country": "Saint Lucia"
+ },
+ {
+ "name": "Cayenne",
+ "lon": -52.33333,
+ "lat": 4.93333,
+ "country": "French Guiana"
+ },
+ {
+ "name": "Charlotte Amalie",
+ "lon": -64.9307,
+ "lat": 18.3419,
+ "country": "U.S. Virgin Islands"
+ },
+ {
+ "name": "Chengdu",
+ "lon": 104.06667,
+ "lat": 30.66667,
+ "country": "China"
+ },
+ {
+ "name": "Chennai",
+ "lon": 80.27847,
+ "lat": 13.08784,
+ "country": "India"
+ },
+ {
+ "name": "Chicago",
+ "lon": -87.65005,
+ "lat": 41.85003,
+ "country": "United States"
+ },
+ {
+ "name": "Chisinau",
+ "lon": 28.8575,
+ "lat": 47.00556,
+ "country": "Moldova"
+ },
+ {
+ "name": "Chittagong",
+ "lon": 91.83168,
+ "lat": 22.3384,
+ "country": "Bangladesh"
+ },
+ {
+ "name": "Chongqing",
+ "lon": 106.55278,
+ "lat": 29.56278,
+ "country": "China"
+ },
+ {
+ "name": "Colombo",
+ "lon": 79.84868,
+ "lat": 6.93548,
+ "country": "Sri Lanka"
+ },
+ {
+ "name": "Conakry",
+ "lon": -13.67729,
+ "lat": 9.53795,
+ "country": "Guinea"
+ },
+ {
+ "name": "Copenhagen",
+ "lon": 12.56553,
+ "lat": 55.67594,
+ "country": "Denmark"
+ },
+ {
+ "name": "Cordoba",
+ "lon": -64.18105,
+ "lat": -31.4135,
+ "country": "Argentina"
+ },
+ {
+ "name": "Curitiba",
+ "lon": -49.27306,
+ "lat": -25.42778,
+ "country": "Brazil"
+ },
+ {
+ "name": "Daegu",
+ "lon": 128.59111,
+ "lat": 35.87028,
+ "country": "South Korea"
+ },
+ {
+ "name": "Daejeon",
+ "lon": 127.38493,
+ "lat": 36.34913,
+ "country": "South Korea"
+ },
+ {
+ "name": "Dakar",
+ "lon": -17.44406,
+ "lat": 14.6937,
+ "country": "Senegal"
+ },
+ {
+ "name": "Dallas",
+ "lon": -96.80667,
+ "lat": 32.78306,
+ "country": "United States"
+ },
+ {
+ "name": "Damascus",
+ "lon": 36.29128,
+ "lat": 33.5102,
+ "country": "Syria"
+ },
+ {
+ "name": "Dar es Salaam",
+ "lon": 39.26951,
+ "lat": -6.82349,
+ "country": "Tanzania"
+ },
+ {
+ "name": "Delhi",
+ "lon": 77.23149,
+ "lat": 28.65195,
+ "country": "India"
+ },
+ {
+ "name": "Denver",
+ "lon": -104.9847,
+ "lat": 39.73915,
+ "country": "United States"
+ },
+ {
+ "name": "Dhaka",
+ "lon": 90.40744,
+ "lat": 23.7104,
+ "country": "Bangladesh"
+ },
+ {
+ "name": "Dili",
+ "lon": 125.57361,
+ "lat": -8.55861,
+ "country": "Timor Leste"
+ },
+ {
+ "name": "Djibouti",
+ "lon": 43.14503,
+ "lat": 11.58901,
+ "country": "Djibouti"
+ },
+ {
+ "name": "Dodoma",
+ "lon": 35.73947,
+ "lat": -6.17221,
+ "country": "Tanzania"
+ },
+ {
+ "name": "Doha",
+ "lon": 51.53096,
+ "lat": 25.28545,
+ "country": "Qatar"
+ },
+ {
+ "name": "Dongguan",
+ "lon": 113.74866,
+ "lat": 23.01797,
+ "country": "China"
+ },
+ {
+ "name": "Douala",
+ "lon": 9.70428,
+ "lat": 4.04827,
+ "country": "Cameroon"
+ },
+ {
+ "name": "Douglas",
+ "lon": -4.48333,
+ "lat": 54.15,
+ "country": "Isle of Man"
+ },
+ {
+ "name": "Dubai",
+ "lon": 55.30927,
+ "lat": 25.07725,
+ "country": "United Arab Emirates"
+ },
+ {
+ "name": "Dublin",
+ "lon": -6.24889,
+ "lat": 53.33306,
+ "country": "Ireland"
+ },
+ {
+ "name": "Durban",
+ "lon": 31.0292,
+ "lat": -29.8579,
+ "country": "South Africa"
+ },
+ {
+ "name": "Dushanbe",
+ "lon": 68.77905,
+ "lat": 38.53575,
+ "country": "Tajikistan"
+ },
+ {
+ "name": "Faisalabad",
+ "lon": 73.08969,
+ "lat": 31.41554,
+ "country": "Pakistan"
+ },
+ {
+ "name": "Fort-de-France",
+ "lon": -61.07418,
+ "lat": 14.60365,
+ "country": "Martinique"
+ },
+ {
+ "name": "Fortaleza",
+ "lon": -38.54306,
+ "lat": -3.71722,
+ "country": "Brazil"
+ },
+ {
+ "name": "Freetown",
+ "lon": -13.2356,
+ "lat": 8.48714,
+ "country": "Sierra Leone"
+ },
+ {
+ "name": "Fukuoka",
+ "lon": 130.41667,
+ "lat": 33.6,
+ "country": "Japan"
+ },
+ {
+ "name": "Funafuti",
+ "lon": 179.19417,
+ "lat": -8.52425,
+ "country": "Tuvalu"
+ },
+ {
+ "name": "Gaborone",
+ "lon": 25.90859,
+ "lat": -24.65451,
+ "country": "Botswana"
+ },
+ {
+ "name": "George Town",
+ "lon": -81.37436,
+ "lat": 19.2866,
+ "country": "Malaysia"
+ },
+ {
+ "name": "Georgetown",
+ "lon": -58.15527,
+ "lat": 6.80448,
+ "country": "Guyana"
+ },
+ {
+ "name": "Gibraltar",
+ "lon": -5.35257,
+ "lat": 36.14474,
+ "country": "Gibraltar"
+ },
+ {
+ "name": "Gitega",
+ "lon": 29.92463,
+ "lat": -3.42708,
+ "country": "Burundi"
+ },
+ {
+ "name": "Giza",
+ "lon": 31.20861,
+ "lat": 30.00944,
+ "country": "Egypt"
+ },
+ {
+ "name": "Guadalajara",
+ "lon": -103.39182,
+ "lat": 20.66682,
+ "country": "Mexico"
+ },
+ {
+ "name": "Guangzhou",
+ "lon": 113.25,
+ "lat": 23.11667,
+ "country": "China"
+ },
+ {
+ "name": "Guatemala City",
+ "lon": -90.51327,
+ "lat": 14.64072,
+ "country": "Guatemala"
+ },
+ {
+ "name": "Guayaquil",
+ "lon": -79.88621,
+ "lat": -2.19616,
+ "country": "Ecuador"
+ },
+ {
+ "name": "Gujranwala",
+ "lon": 74.18705,
+ "lat": 32.15567,
+ "country": "Pakistan"
+ },
+ {
+ "name": "Gustavia",
+ "lon": -62.84978,
+ "lat": 17.89618,
+ "country": "Saint Barthelemy"
+ },
+ {
+ "name": "Gwangju",
+ "lon": 126.91556,
+ "lat": 35.15472,
+ "country": "South Korea"
+ },
+ {
+ "name": "Hamburg",
+ "lon": 9.99302,
+ "lat": 53.55073,
+ "country": "Germany"
+ },
+ {
+ "name": "Hanoi",
+ "lon": 105.84117,
+ "lat": 21.0245,
+ "country": "Vietnam"
+ },
+ {
+ "name": "Harare",
+ "lon": 31.05337,
+ "lat": -17.82772,
+ "country": "Zimbabwe"
+ },
+ {
+ "name": "Havana",
+ "lon": -82.38304,
+ "lat": 23.13302,
+ "country": "Cuba"
+ },
+ {
+ "name": "Helsinki",
+ "lon": 24.93545,
+ "lat": 60.16952,
+ "country": "Finland"
+ },
+ {
+ "name": "Ho Chi Minh City",
+ "lon": 106.62965,
+ "lat": 10.82302,
+ "country": "Vietnam"
+ },
+ {
+ "name": "Hong Kong",
+ "lon": 114.17469,
+ "lat": 22.27832,
+ "country": "Hong Kong"
+ },
+ {
+ "name": "Honiara",
+ "lon": 159.95,
+ "lat": -9.43333,
+ "country": "Solomon Islands"
+ },
+ {
+ "name": "Honolulu",
+ "lon": -157.85833,
+ "lat": 21.30694,
+ "country": "United States"
+ },
+ {
+ "name": "Houston",
+ "lon": -95.36327,
+ "lat": 29.76328,
+ "country": "United States"
+ },
+ {
+ "name": "Hyderabad",
+ "lon": 78.45636,
+ "lat": 17.38405,
+ "country": "India"
+ },
+ {
+ "name": "Hyderabad",
+ "lon": 68.37366,
+ "lat": 25.39242,
+ "country": "Pakistan"
+ },
+ {
+ "name": "Ibadan",
+ "lon": 3.90591,
+ "lat": 7.37756,
+ "country": "Nigeria"
+ },
+ {
+ "name": "Incheon",
+ "lon": 126.70515,
+ "lat": 37.45646,
+ "country": "South Korea"
+ },
+ {
+ "name": "Isfahan",
+ "lon": 51.67462,
+ "lat": 32.65246,
+ "country": "Iran"
+ },
+ {
+ "name": "Islamabad",
+ "lon": 73.04329,
+ "lat": 33.72148,
+ "country": "Pakistan"
+ },
+ {
+ "name": "Istanbul",
+ "lon": 28.94966,
+ "lat": 41.01384,
+ "country": "Turkey"
+ },
+ {
+ "name": "Izmir",
+ "lon": 27.13838,
+ "lat": 38.41273,
+ "country": "Turkey"
+ },
+ {
+ "name": "Jaipur",
+ "lon": 75.78781,
+ "lat": 26.91962,
+ "country": "India"
+ },
+ {
+ "name": "Jakarta",
+ "lon": 106.84513,
+ "lat": -6.21462,
+ "country": "Indonesia"
+ },
+ {
+ "name": "Jeddah",
+ "lon": 39.18624,
+ "lat": 21.49012,
+ "country": "Saudi Arabia"
+ },
+ {
+ "name": "Jerusalem",
+ "lon": 35.21633,
+ "lat": 31.76904,
+ "country": "Israel"
+ },
+ {
+ "name": "Johannesburg",
+ "lon": 28.04363,
+ "lat": -26.20227,
+ "country": "South Africa"
+ },
+ {
+ "name": "Juarez",
+ "lon": -106.46084,
+ "lat": 31.72024,
+ "country": "Mexico"
+ },
+ {
+ "name": "Juba",
+ "lon": 31.58247,
+ "lat": 4.85165,
+ "country": "South Sudan"
+ },
+ {
+ "name": "Kabul",
+ "lon": 69.17233,
+ "lat": 34.52813,
+ "country": "Afghanistan"
+ },
+ {
+ "name": "Kaduna",
+ "lon": 7.43879,
+ "lat": 10.52641,
+ "country": "Nigeria"
+ },
+ {
+ "name": "Kampala",
+ "lon": 32.58219,
+ "lat": 0.31628,
+ "country": "Uganda"
+ },
+ {
+ "name": "Kano",
+ "lon": 8.51672,
+ "lat": 12.00012,
+ "country": "Nigeria"
+ },
+ {
+ "name": "Kanpur",
+ "lon": 80.34975,
+ "lat": 26.46523,
+ "country": "India"
+ },
+ {
+ "name": "Kaohsiung",
+ "lon": 120.31333,
+ "lat": 22.61626,
+ "country": "Taiwan"
+ },
+ {
+ "name": "Karachi",
+ "lon": 67.0104,
+ "lat": 24.8608,
+ "country": "Pakistan"
+ },
+ {
+ "name": "Karaj",
+ "lon": 50.99155,
+ "lat": 35.83266,
+ "country": "Iran"
+ },
+ {
+ "name": "Kathmandu",
+ "lon": 85.3206,
+ "lat": 27.70169,
+ "country": "Nepal"
+ },
+ {
+ "name": "Kawasaki",
+ "lon": 139.71722,
+ "lat": 35.52056,
+ "country": "Japan"
+ },
+ {
+ "name": "Kharkiv",
+ "lon": 36.25272,
+ "lat": 49.98081,
+ "country": "Ukraine"
+ },
+ {
+ "name": "Khartoum",
+ "lon": 32.53241,
+ "lat": 15.55177,
+ "country": "Sudan"
+ },
+ {
+ "name": "Khulna",
+ "lon": 89.56439,
+ "lat": 22.80979,
+ "country": "Bangladesh"
+ },
+ {
+ "name": "Kigali",
+ "lon": 30.05885,
+ "lat": -1.94995,
+ "country": "Rwanda"
+ },
+ {
+ "name": "Kingsburg",
+ "lon": -119.554,
+ "lat": 36.5138,
+ "country": "United States"
+ },
+ {
+ "name": "Kingston",
+ "lon": -76.79358,
+ "lat": 17.99702,
+ "country": "Jamaica"
+ },
+ {
+ "name": "Kingstown",
+ "lon": -61.22742,
+ "lat": 13.15527,
+ "country": "Saint Vincent and the Grenadines"
+ },
+ {
+ "name": "Kinshasa",
+ "lon": 15.31357,
+ "lat": -4.32758,
+ "country": "Democratic Republic of the Congo"
+ },
+ {
+ "name": "Kobe",
+ "lon": 135.183,
+ "lat": 34.6913,
+ "country": "Japan"
+ },
+ {
+ "name": "Kolkata",
+ "lon": 88.36304,
+ "lat": 22.56263,
+ "country": "India"
+ },
+ {
+ "name": "Kota Bharu",
+ "lon": 102.24333,
+ "lat": 6.12361,
+ "country": "Malaysia"
+ },
+ {
+ "name": "Kowloon",
+ "lon": 114.18333,
+ "lat": 22.31667,
+ "country": "Hong Kong"
+ },
+ {
+ "name": "Kuala Lumpur",
+ "lon": 101.68653,
+ "lat": 3.1412,
+ "country": "Malaysia"
+ },
+ {
+ "name": "Kumasi",
+ "lon": -1.62443,
+ "lat": 6.68848,
+ "country": "Ghana"
+ },
+ {
+ "name": "Kuwait",
+ "lon": 47.97833,
+ "lat": 29.36972,
+ "country": "Kuwait"
+ },
+ {
+ "name": "Kyiv",
+ "lon": 30.5238,
+ "lat": 50.45466,
+ "country": "Ukraine"
+ },
+ {
+ "name": "Kyoto",
+ "lon": 135.75385,
+ "lat": 35.02107,
+ "country": "Japan"
+ },
+ {
+ "name": "La Paz",
+ "lon": -68.15,
+ "lat": -16.5,
+ "country": "Bolivia"
+ },
+ {
+ "name": "Lagos",
+ "lon": 3.39467,
+ "lat": 6.45407,
+ "country": "Nigeria"
+ },
+ {
+ "name": "Lahore",
+ "lon": 74.35071,
+ "lat": 31.558,
+ "country": "Pakistan"
+ },
+ {
+ "name": "Libreville",
+ "lon": 9.45356,
+ "lat": 0.39241,
+ "country": "Gabon"
+ },
+ {
+ "name": "Lilongwe",
+ "lon": 33.78725,
+ "lat": -13.96692,
+ "country": "Malawi"
+ },
+ {
+ "name": "Lima",
+ "lon": -77.02824,
+ "lat": -12.04318,
+ "country": "Peru"
+ },
+ {
+ "name": "Lisbon",
+ "lon": -9.13333,
+ "lat": 38.71667,
+ "country": "Portugal"
+ },
+ {
+ "name": "Ljubljana",
+ "lon": 14.50513,
+ "lat": 46.05108,
+ "country": "Slovenia"
+ },
+ {
+ "name": "Lome",
+ "lon": 1.22154,
+ "lat": 6.12874,
+ "country": "Togo"
+ },
+ {
+ "name": "London",
+ "lon": -0.12574,
+ "lat": 51.50853,
+ "country": "United Kingdom"
+ },
+ {
+ "name": "Los Angeles",
+ "lon": -118.24368,
+ "lat": 34.05223,
+ "country": "United States"
+ },
+ {
+ "name": "Luanda",
+ "lon": 13.23432,
+ "lat": -8.83682,
+ "country": "Angola"
+ },
+ {
+ "name": "Lubumbashi",
+ "lon": 27.47938,
+ "lat": -11.66089,
+ "country": "Democratic Republic of the Congo"
+ },
+ {
+ "name": "Lusaka",
+ "lon": 28.28713,
+ "lat": -15.40669,
+ "country": "Zambia"
+ },
+ {
+ "name": "Luxembourg",
+ "lon": 6.13,
+ "lat": 49.61167,
+ "country": "Luxembourg"
+ },
+ {
+ "name": "Macau",
+ "lon": 113.54611,
+ "lat": 22.20056,
+ "country": "Macao"
+ },
+ {
+ "name": "Madrid",
+ "lon": -3.70256,
+ "lat": 40.4165,
+ "country": "Spain"
+ },
+ {
+ "name": "Majuro",
+ "lon": 171.38027,
+ "lat": 7.08971,
+ "country": "Marshall Islands"
+ },
+ {
+ "name": "Makassar",
+ "lon": 119.43194,
+ "lat": -5.14861,
+ "country": "Indonesia"
+ },
+ {
+ "name": "Malabo",
+ "lon": 8.78166,
+ "lat": 3.75578,
+ "country": "Equatorial Guinea"
+ },
+ {
+ "name": "Male",
+ "lon": 73.50916,
+ "lat": 4.17521,
+ "country": "Maldives"
+ },
+ {
+ "name": "Mamoudzou",
+ "lon": 45.22878,
+ "lat": -12.78234,
+ "country": "Mayotte"
+ },
+ {
+ "name": "Managua",
+ "lon": -86.2504,
+ "lat": 12.13282,
+ "country": "Nicaragua"
+ },
+ {
+ "name": "Manama",
+ "lon": 50.58565,
+ "lat": 26.22787,
+ "country": "Bahrain"
+ },
+ {
+ "name": "Manaus",
+ "lon": -60.025,
+ "lat": -3.10194,
+ "country": "Brazil"
+ },
+ {
+ "name": "Manila",
+ "lon": 120.9822,
+ "lat": 14.6042,
+ "country": "Philippines"
+ },
+ {
+ "name": "Maputo",
+ "lon": 32.58322,
+ "lat": -25.96553,
+ "country": "Mozambique"
+ },
+ {
+ "name": "Maracaibo",
+ "lon": -71.61245,
+ "lat": 10.66663,
+ "country": "Venezuela"
+ },
+ {
+ "name": "Maracay",
+ "lon": -67.59113,
+ "lat": 10.23535,
+ "country": "Venezuela"
+ },
+ {
+ "name": "Mariehamn",
+ "lon": 19.93481,
+ "lat": 60.09726,
+ "country": "Aland Islands"
+ },
+ {
+ "name": "Marigot",
+ "lon": -63.08302,
+ "lat": 18.06819,
+ "country": "Saint Martin"
+ },
+ {
+ "name": "Maseru",
+ "lon": 27.48333,
+ "lat": -29.31667,
+ "country": "Lesotho"
+ },
+ {
+ "name": "Mashhad",
+ "lon": 59.56796,
+ "lat": 36.31559,
+ "country": "Iran"
+ },
+ {
+ "name": "Mbabane",
+ "lon": 31.13333,
+ "lat": -26.31667,
+ "country": "Eswatini"
+ },
+ {
+ "name": "Mecca",
+ "lon": 39.82563,
+ "lat": 21.42664,
+ "country": "Saudi Arabia"
+ },
+ {
+ "name": "Medan",
+ "lon": 98.66667,
+ "lat": 3.58333,
+ "country": "Indonesia"
+ },
+ {
+ "name": "Medellin",
+ "lon": -75.56359,
+ "lat": 6.25184,
+ "country": "Colombia"
+ },
+ {
+ "name": "Medina",
+ "lon": 39.61417,
+ "lat": 24.46861,
+ "country": "Saudi Arabia"
+ },
+ {
+ "name": "Melbourne",
+ "lon": 144.96332,
+ "lat": -37.814,
+ "country": "Australia"
+ },
+ {
+ "name": "Mexico City",
+ "lon": -99.12766,
+ "lat": 19.42847,
+ "country": "Mexico"
+ },
+ {
+ "name": "Miami",
+ "lon": -80.19366,
+ "lat": 25.77427,
+ "country": "United States"
+ },
+ {
+ "name": "Minsk",
+ "lon": 27.56667,
+ "lat": 53.9,
+ "country": "Belarus"
+ },
+ {
+ "name": "Mogadishu",
+ "lon": 45.34375,
+ "lat": 2.03711,
+ "country": "Somalia"
+ },
+ {
+ "name": "Monaco",
+ "lon": 7.41667,
+ "lat": 43.73333,
+ "country": "Monaco"
+ },
+ {
+ "name": "Monrovia",
+ "lon": -10.7969,
+ "lat": 6.30054,
+ "country": "Liberia"
+ },
+ {
+ "name": "Montevideo",
+ "lon": -56.18816,
+ "lat": -34.90328,
+ "country": "Uruguay"
+ },
+ {
+ "name": "Montreal",
+ "lon": -73.58781,
+ "lat": 45.50884,
+ "country": "Canada"
+ },
+ {
+ "name": "Moroni",
+ "lon": 43.25506,
+ "lat": -11.70216,
+ "country": "Comoros"
+ },
+ {
+ "name": "Moscow",
+ "lon": 37.61556,
+ "lat": 55.75222,
+ "country": "Russia"
+ },
+ {
+ "name": "Mosul",
+ "lon": 43.11889,
+ "lat": 36.335,
+ "country": "Iraq"
+ },
+ {
+ "name": "Multan",
+ "lon": 71.47824,
+ "lat": 30.19679,
+ "country": "Pakistan"
+ },
+ {
+ "name": "Mumbai",
+ "lon": 72.88261,
+ "lat": 19.07283,
+ "country": "India"
+ },
+ {
+ "name": "Muscat",
+ "lon": 58.40778,
+ "lat": 23.58413,
+ "country": "Oman"
+ },
+ {
+ "name": "N'Djamena",
+ "lon": 15.0444,
+ "lat": 12.10672,
+ "country": "Chad"
+ },
+ {
+ "name": "Nagoya",
+ "lon": 136.90641,
+ "lat": 35.18147,
+ "country": "Japan"
+ },
+ {
+ "name": "Nairobi",
+ "lon": 36.81667,
+ "lat": -1.28333,
+ "country": "Kenya"
+ },
+ {
+ "name": "Nanchong",
+ "lon": 106.08473,
+ "lat": 30.79508,
+ "country": "China"
+ },
+ {
+ "name": "Nanjing",
+ "lon": 118.77778,
+ "lat": 32.06167,
+ "country": "China"
+ },
+ {
+ "name": "Nassau",
+ "lon": -77.34306,
+ "lat": 25.05823,
+ "country": "Bahamas"
+ },
+ {
+ "name": "Nay Pyi Taw",
+ "lon": 96.12972,
+ "lat": 19.745,
+ "country": "Myanmar"
+ },
+ {
+ "name": "New York",
+ "lon": -74.00597,
+ "lat": 40.71427,
+ "country": "United States"
+ },
+ {
+ "name": "Niamey",
+ "lon": 2.1098,
+ "lat": 13.51366,
+ "country": "Niger"
+ },
+ {
+ "name": "Nicosia",
+ "lon": 33.3642,
+ "lat": 35.17531,
+ "country": "Cyprus"
+ },
+ {
+ "name": "Nouakchott",
+ "lon": -15.9785,
+ "lat": 18.08581,
+ "country": "Mauritania"
+ },
+ {
+ "name": "Noumea",
+ "lon": 166.44884,
+ "lat": -22.27407,
+ "country": "New Caledonia"
+ },
+ {
+ "name": "Novosibirsk",
+ "lon": 82.9346,
+ "lat": 55.0415,
+ "country": "Russia"
+ },
+ {
+ "name": "Nuku'alofa",
+ "lon": -175.2018,
+ "lat": -21.13938,
+ "country": "Tonga"
+ },
+ {
+ "name": "Nur-Sultan",
+ "lon": 71.44598,
+ "lat": 51.1801,
+ "country": "Kazakhstan"
+ },
+ {
+ "name": "Nuuk",
+ "lon": -51.72157,
+ "lat": 64.18347,
+ "country": "Greenland"
+ },
+ {
+ "name": "Oranjestad",
+ "lon": -70.02703,
+ "lat": 12.52398,
+ "country": "Aruba"
+ },
+ {
+ "name": "Osaka",
+ "lon": 135.50218,
+ "lat": 34.69374,
+ "country": "Japan"
+ },
+ {
+ "name": "Oslo",
+ "lon": 10.74609,
+ "lat": 59.91273,
+ "country": "Norway"
+ },
+ {
+ "name": "Ottawa",
+ "lon": -75.69812,
+ "lat": 45.41117,
+ "country": "Canada"
+ },
+ {
+ "name": "Ouagadougou",
+ "lon": -1.53388,
+ "lat": 12.36566,
+ "country": "Burkina Faso"
+ },
+ {
+ "name": "Pago Pago",
+ "lon": -170.7025,
+ "lat": -14.27806,
+ "country": "American Samoa"
+ },
+ {
+ "name": "Palembang",
+ "lon": 104.7458,
+ "lat": -2.91673,
+ "country": "Indonesia"
+ },
+ {
+ "name": "Palo Alto",
+ "lon": -122.14302,
+ "lat": 37.44188,
+ "country": "United States"
+ },
+ {
+ "name": "Panama",
+ "lon": -79.51973,
+ "lat": 8.9936,
+ "country": "Panama"
+ },
+ {
+ "name": "Papeete",
+ "lon": -149.5665,
+ "lat": -17.53733,
+ "country": "French Polynesia"
+ },
+ {
+ "name": "Paramaribo",
+ "lon": -55.16682,
+ "lat": 5.86638,
+ "country": "Suriname"
+ },
+ {
+ "name": "Paris",
+ "lon": 2.3488,
+ "lat": 48.85341,
+ "country": "France"
+ },
+ {
+ "name": "Perth",
+ "lon": 115.8614,
+ "lat": -31.95224,
+ "country": "Australia"
+ },
+ {
+ "name": "Philadelphia",
+ "lon": -75.16379,
+ "lat": 39.95233,
+ "country": "United States"
+ },
+ {
+ "name": "Phnom Penh",
+ "lon": 104.91601,
+ "lat": 11.56245,
+ "country": "Cambodia"
+ },
+ {
+ "name": "Phoenix",
+ "lon": -112.07404,
+ "lat": 33.44838,
+ "country": "United States"
+ },
+ {
+ "name": "Podgorica",
+ "lon": 19.26361,
+ "lat": 42.44111,
+ "country": "Montenegro"
+ },
+ {
+ "name": "Port Louis",
+ "lon": 57.49889,
+ "lat": -20.16194,
+ "country": "Mauritius"
+ },
+ {
+ "name": "Port Moresby",
+ "lon": 147.15089,
+ "lat": -9.47723,
+ "country": "Papua New Guinea"
+ },
+ {
+ "name": "Port of Spain",
+ "lon": -61.51889,
+ "lat": 10.66668,
+ "country": "Trinidad and Tobago"
+ },
+ {
+ "name": "Port-Vila",
+ "lon": 168.31366,
+ "lat": -17.73648,
+ "country": "Vanuatu"
+ },
+ {
+ "name": "Port-au-Prince",
+ "lon": -72.33881,
+ "lat": 18.54349,
+ "country": "Haiti"
+ },
+ {
+ "name": "Porto Alegre",
+ "lon": -51.23019,
+ "lat": -30.03283,
+ "country": "Brazil"
+ },
+ {
+ "name": "Porto-Novo",
+ "lon": 2.60359,
+ "lat": 6.49646,
+ "country": "Benin"
+ },
+ {
+ "name": "Prague",
+ "lon": 14.42076,
+ "lat": 50.08804,
+ "country": "Czechia"
+ },
+ {
+ "name": "Praia",
+ "lon": -23.51254,
+ "lat": 14.93152,
+ "country": "Cabo Verde"
+ },
+ {
+ "name": "Pretoria",
+ "lon": 28.18783,
+ "lat": -25.74486,
+ "country": "South Africa"
+ },
+ {
+ "name": "Pristina",
+ "lon": 21.16688,
+ "lat": 42.67272,
+ "country": "Kosovo"
+ },
+ {
+ "name": "Puebla",
+ "lon": -98.20346,
+ "lat": 19.03793,
+ "country": "Mexico"
+ },
+ {
+ "name": "Pune",
+ "lon": 73.85535,
+ "lat": 18.51957,
+ "country": "India"
+ },
+ {
+ "name": "Pyongyang",
+ "lon": 125.75432,
+ "lat": 39.03385,
+ "country": "North Korea"
+ },
+ {
+ "name": "Quezon City",
+ "lon": 121.0509,
+ "lat": 14.6488,
+ "country": "Philippines"
+ },
+ {
+ "name": "Quito",
+ "lon": -78.52495,
+ "lat": -0.22985,
+ "country": "Ecuador"
+ },
+ {
+ "name": "Rabat",
+ "lon": -6.83255,
+ "lat": 34.01325,
+ "country": "Morocco"
+ },
+ {
+ "name": "Rawalpindi",
+ "lon": 73.0479,
+ "lat": 33.59733,
+ "country": "Pakistan"
+ },
+ {
+ "name": "Recife",
+ "lon": -34.88111,
+ "lat": -8.05389,
+ "country": "Brazil"
+ },
+ {
+ "name": "Reykjavik",
+ "lon": -21.89541,
+ "lat": 64.13548,
+ "country": "Iceland"
+ },
+ {
+ "name": "Riga",
+ "lon": 24.10589,
+ "lat": 56.946,
+ "country": "Latvia"
+ },
+ {
+ "name": "Rio de Janeiro",
+ "lon": -43.18223,
+ "lat": -22.90642,
+ "country": "Brazil"
+ },
+ {
+ "name": "Riyadh",
+ "lon": 46.72185,
+ "lat": 24.68773,
+ "country": "Saudi Arabia"
+ },
+ {
+ "name": "Road Town",
+ "lon": -64.62079,
+ "lat": 18.42693,
+ "country": "British Virgin Islands"
+ },
+ {
+ "name": "Rome",
+ "lon": 12.51133,
+ "lat": 41.89193,
+ "country": "Italy"
+ },
+ {
+ "name": "Roseau",
+ "lon": -61.38808,
+ "lat": 15.30174,
+ "country": "Dominica"
+ },
+ {
+ "name": "Saint George's",
+ "lon": -61.75226,
+ "lat": 12.05288,
+ "country": "Grenada"
+ },
+ {
+ "name": "Saint Helier",
+ "lon": -2.10491,
+ "lat": 49.18804,
+ "country": "Jersey"
+ },
+ {
+ "name": "Saint John's",
+ "lon": -61.84329,
+ "lat": 17.12096,
+ "country": "Antigua and Barbuda"
+ },
+ {
+ "name": "Saint Peter Port",
+ "lon": -2.53527,
+ "lat": 49.45981,
+ "country": "Guernsey"
+ },
+ {
+ "name": "Saint Petersburg",
+ "lon": 30.31413,
+ "lat": 59.93863,
+ "country": "Russia"
+ },
+ {
+ "name": "Saint-Denis",
+ "lon": 55.4504,
+ "lat": -20.88231,
+ "country": "Reunion"
+ },
+ {
+ "name": "Saint-Pierre",
+ "lon": -56.1773,
+ "lat": 46.77914,
+ "country": "Reunion"
+ },
+ {
+ "name": "Saipan",
+ "lon": 145.7545,
+ "lat": 15.21233,
+ "country": "Northern Mariana Islands"
+ },
+ {
+ "name": "Salvador",
+ "lon": -38.51083,
+ "lat": -12.97111,
+ "country": "Brazil"
+ },
+ {
+ "name": "San Antonio",
+ "lon": -98.49363,
+ "lat": 29.42412,
+ "country": "United States"
+ },
+ {
+ "name": "San Diego",
+ "lon": -117.16472,
+ "lat": 32.71571,
+ "country": "United States"
+ },
+ {
+ "name": "San Francisco",
+ "lon": -122.41942,
+ "lat": 37.77493,
+ "country": "United States"
+ },
+ {
+ "name": "San Jose",
+ "lon": -84.08333,
+ "lat": 9.93333,
+ "country": "United States"
+ },
+ {
+ "name": "San Juan",
+ "lon": -66.10572,
+ "lat": 18.46633,
+ "country": "Argentina"
+ },
+ {
+ "name": "San Marino",
+ "lon": 12.44639,
+ "lat": 43.93667,
+ "country": "United States"
+ },
+ {
+ "name": "San Salvador",
+ "lon": -89.18718,
+ "lat": 13.68935,
+ "country": "El Salvador"
+ },
+ {
+ "name": "Sanaa",
+ "lon": 44.20667,
+ "lat": 15.35472,
+ "country": "Yemen"
+ },
+ {
+ "name": "Santa Cruz de la Sierra",
+ "lon": -63.18117,
+ "lat": -17.78629,
+ "country": "Bolivia"
+ },
+ {
+ "name": "Santiago",
+ "lon": -70.64827,
+ "lat": -33.45694,
+ "country": "Chile"
+ },
+ {
+ "name": "Santo Domingo",
+ "lon": -69.89232,
+ "lat": 18.47186,
+ "country": "Dominican Republic"
+ },
+ {
+ "name": "Sao Paulo",
+ "lon": -46.63611,
+ "lat": -23.5475,
+ "country": "Brazil"
+ },
+ {
+ "name": "Sao Tome",
+ "lon": 6.72732,
+ "lat": 0.33654,
+ "country": "Sao Tome and Principe"
+ },
+ {
+ "name": "Sapporo",
+ "lon": 141.35,
+ "lat": 43.06667,
+ "country": "Japan"
+ },
+ {
+ "name": "Sarajevo",
+ "lon": 18.35644,
+ "lat": 43.84864,
+ "country": "Bosnia and Herzegovina"
+ },
+ {
+ "name": "Seattle",
+ "lon": -122.33207,
+ "lat": 47.60621,
+ "country": "United States"
+ },
+ {
+ "name": "Semarang",
+ "lon": 110.42083,
+ "lat": -6.99306,
+ "country": "Indonesia"
+ },
+ {
+ "name": "Seoul",
+ "lon": 126.9784,
+ "lat": 37.566,
+ "country": "South Korea"
+ },
+ {
+ "name": "Shanghai",
+ "lon": 121.45806,
+ "lat": 31.22222,
+ "country": "China"
+ },
+ {
+ "name": "Sharjah",
+ "lon": 55.41206,
+ "lat": 25.33737,
+ "country": "United Arab Emirates"
+ },
+ {
+ "name": "Shenzhen",
+ "lon": 114.0683,
+ "lat": 22.54554,
+ "country": "China"
+ },
+ {
+ "name": "Singapore",
+ "lon": 103.85007,
+ "lat": 1.28967,
+ "country": "Singapore"
+ },
+ {
+ "name": "Skopje",
+ "lon": 21.43141,
+ "lat": 41.99646,
+ "country": "North Macedonia"
+ },
+ {
+ "name": "Sofia",
+ "lon": 23.32415,
+ "lat": 42.69751,
+ "country": "Bulgaria"
+ },
+ {
+ "name": "South Tangerang",
+ "lon": 106.71789,
+ "lat": -6.28862,
+ "country": "Indonesia"
+ },
+ {
+ "name": "Soweto",
+ "lon": 27.85849,
+ "lat": -26.26781,
+ "country": "South Africa"
+ },
+ {
+ "name": "Stockholm",
+ "lon": 18.06871,
+ "lat": 59.32938,
+ "country": "Sweden"
+ },
+ {
+ "name": "Sucre",
+ "lon": -65.26274,
+ "lat": -19.03332,
+ "country": "Bolivia"
+ },
+ {
+ "name": "Surabaya",
+ "lon": 112.75083,
+ "lat": -7.24917,
+ "country": "Indonesia"
+ },
+ {
+ "name": "Surat",
+ "lon": 72.83023,
+ "lat": 21.19594,
+ "country": "India"
+ },
+ {
+ "name": "Suva",
+ "lon": 178.44149,
+ "lat": -18.14161,
+ "country": "Fiji"
+ },
+ {
+ "name": "Sydney",
+ "lon": 151.20732,
+ "lat": -33.86785,
+ "country": "Australia"
+ },
+ {
+ "name": "Tabriz",
+ "lon": 46.2919,
+ "lat": 38.08,
+ "country": "Iran"
+ },
+ {
+ "name": "Taipei",
+ "lon": 121.53185,
+ "lat": 25.04776,
+ "country": "Taiwan"
+ },
+ {
+ "name": "Tallinn",
+ "lon": 24.75353,
+ "lat": 59.43696,
+ "country": "Estonia"
+ },
+ {
+ "name": "Tangerang",
+ "lon": 106.63,
+ "lat": -6.17806,
+ "country": "Indonesia"
+ },
+ {
+ "name": "Tarawa",
+ "lon": 172.97696,
+ "lat": 1.3278,
+ "country": "Kiribati"
+ },
+ {
+ "name": "Tashkent",
+ "lon": 69.21627,
+ "lat": 41.26465,
+ "country": "Uzbekistan"
+ },
+ {
+ "name": "Tbilisi",
+ "lon": 44.83368,
+ "lat": 41.69411,
+ "country": "Georgia"
+ },
+ {
+ "name": "Tegucigalpa",
+ "lon": -87.20681,
+ "lat": 14.0818,
+ "country": "Honduras"
+ },
+ {
+ "name": "Tehran",
+ "lon": 51.42151,
+ "lat": 35.69439,
+ "country": "Iran"
+ },
+ {
+ "name": "Tel Aviv",
+ "lon": 34.78057,
+ "lat": 32.08088,
+ "country": "Israel"
+ },
+ {
+ "name": "Thimphu",
+ "lon": 89.64191,
+ "lat": 27.46609,
+ "country": "Bhutan"
+ },
+ {
+ "name": "Tianjin",
+ "lon": 117.17667,
+ "lat": 39.14222,
+ "country": "China"
+ },
+ {
+ "name": "Tijuana",
+ "lon": -117.00371,
+ "lat": 32.5027,
+ "country": "Mexico"
+ },
+ {
+ "name": "Tirana",
+ "lon": 19.81889,
+ "lat": 41.3275,
+ "country": "Albania"
+ },
+ {
+ "name": "Tokyo",
+ "lon": 139.69171,
+ "lat": 35.6895,
+ "country": "Japan"
+ },
+ {
+ "name": "Toronto",
+ "lon": -79.4163,
+ "lat": 43.70011,
+ "country": "Canada"
+ },
+ {
+ "name": "Torshavn",
+ "lon": -6.77164,
+ "lat": 62.00973,
+ "country": "Faroe Islands"
+ },
+ {
+ "name": "Tripoli",
+ "lon": 13.18733,
+ "lat": 32.88743,
+ "country": "Libya"
+ },
+ {
+ "name": "Tunis",
+ "lon": 10.16579,
+ "lat": 36.81897,
+ "country": "Tunisia"
+ },
+ {
+ "name": "Ulan Bator",
+ "lon": 106.88324,
+ "lat": 47.90771,
+ "country": "Mongolia"
+ },
+ {
+ "name": "Vaduz",
+ "lon": 9.52154,
+ "lat": 47.14151,
+ "country": "Liechtenstein"
+ },
+ {
+ "name": "Valencia",
+ "lon": -68.00765,
+ "lat": 10.16202,
+ "country": "Venezuela"
+ },
+ {
+ "name": "Valletta",
+ "lon": 14.5148,
+ "lat": 35.89968,
+ "country": "Malta"
+ },
+ {
+ "name": "Vancouver",
+ "lon": -123.11934,
+ "lat": 49.24966,
+ "country": "Canada"
+ },
+ {
+ "name": "Victoria",
+ "lon": 55.45501,
+ "lat": -4.62001,
+ "country": "Canada"
+ },
+ {
+ "name": "Vienna",
+ "lon": 16.37208,
+ "lat": 48.20849,
+ "country": "Austria"
+ },
+ {
+ "name": "Vientiane",
+ "lon": 102.6,
+ "lat": 17.96667,
+ "country": "Laos"
+ },
+ {
+ "name": "Vilnius",
+ "lon": 25.2798,
+ "lat": 54.68916,
+ "country": "Lithuania"
+ },
+ {
+ "name": "Warsaw",
+ "lon": 21.01178,
+ "lat": 52.22977,
+ "country": "Poland"
+ },
+ {
+ "name": "Washington",
+ "lon": -77.03637,
+ "lat": 38.89511,
+ "country": "United States"
+ },
+ {
+ "name": "Wellington",
+ "lon": 174.77557,
+ "lat": -41.28664,
+ "country": "New Zealand"
+ },
+ {
+ "name": "Willemstad",
+ "lon": -68.93354,
+ "lat": 12.1084,
+ "country": "Curacao"
+ },
+ {
+ "name": "Windhoek",
+ "lon": 17.08323,
+ "lat": -22.55941,
+ "country": "Namibia"
+ },
+ {
+ "name": "Wuhan",
+ "lon": 114.26667,
+ "lat": 30.58333,
+ "country": "China"
+ },
+ {
+ "name": "Xi'an",
+ "lon": 108.92861,
+ "lat": 34.25833,
+ "country": "China"
+ },
+ {
+ "name": "Yamoussoukro",
+ "lon": -5.27674,
+ "lat": 6.82055,
+ "country": "Ivory Coast"
+ },
+ {
+ "name": "Yangon",
+ "lon": 96.15611,
+ "lat": 16.80528,
+ "country": "Myanmar"
+ },
+ {
+ "name": "Yaounde",
+ "lon": 11.51667,
+ "lat": 3.86667,
+ "country": "Cameroon"
+ },
+ {
+ "name": "Yekaterinburg",
+ "lon": 60.6122,
+ "lat": 56.8519,
+ "country": "Russia"
+ },
+ {
+ "name": "Yerevan",
+ "lon": 44.51361,
+ "lat": 40.18111,
+ "country": "Armenia"
+ },
+ {
+ "name": "Yokohama",
+ "lon": 139.65,
+ "lat": 35.43333,
+ "country": "Japan"
+ },
+ {
+ "name": "Zagreb",
+ "lon": 15.97798,
+ "lat": 45.81444,
+ "country": "Croatia"
+ }
+]
\ No newline at end of file
diff --git a/src/main/resources/weatherAPI.json b/src/main/resources/weatherAPI.json
new file mode 100644
index 000000000..649d9cfc4
--- /dev/null
+++ b/src/main/resources/weatherAPI.json
@@ -0,0 +1,1345 @@
+{
+ "city" : {
+ "country" : "CA",
+ "coord" : {
+ "lon" : -79.4163,
+ "lat" : 43.7001
+ },
+ "sunrise" : 1732710456,
+ "timezone" : -18000,
+ "sunset" : 1732743824,
+ "name" : "Toronto",
+ "id" : 6167865,
+ "population" : 4612191
+ },
+ "cnt" : 40,
+ "cod" : "200",
+ "message" : 0,
+ "list" : [ {
+ "dt" : 1732752000,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-28 00:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : 4.05,
+ "temp_min" : 4.03,
+ "grnd_level" : 996,
+ "temp_kf" : 0.02,
+ "humidity" : 55,
+ "pressure" : 1013,
+ "sea_level" : 1013,
+ "feels_like" : 1.61,
+ "temp_max" : 4.05
+ },
+ "clouds" : {
+ "all" : 80
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 258,
+ "speed" : 2.69,
+ "gust" : 5.92
+ }
+ }, {
+ "dt" : 1732762800,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-28 03:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 3.53,
+ "temp_min" : 3.27,
+ "grnd_level" : 996,
+ "temp_kf" : 0.26,
+ "humidity" : 54,
+ "pressure" : 1012,
+ "sea_level" : 1012,
+ "feels_like" : 1.96,
+ "temp_max" : 3.53
+ },
+ "clouds" : {
+ "all" : 92
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 278,
+ "speed" : 1.74,
+ "gust" : 3.73
+ }
+ }, {
+ "dt" : 1732773600,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-28 06:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 3.12,
+ "temp_min" : 3.12,
+ "grnd_level" : 994,
+ "temp_kf" : 0,
+ "humidity" : 52,
+ "pressure" : 1010,
+ "sea_level" : 1010,
+ "feels_like" : 3.12,
+ "temp_max" : 3.12
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 328,
+ "speed" : 1.14,
+ "gust" : 1.77
+ }
+ }, {
+ "dt" : 1732784400,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-28 09:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 3.16,
+ "temp_min" : 3.16,
+ "grnd_level" : 992,
+ "temp_kf" : 0,
+ "humidity" : 53,
+ "pressure" : 1008,
+ "sea_level" : 1008,
+ "feels_like" : 3.16,
+ "temp_max" : 3.16
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 68,
+ "speed" : 0.87,
+ "gust" : 1.2
+ }
+ }, {
+ "dt" : 1732795200,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-28 12:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 3.04,
+ "temp_min" : 3.04,
+ "grnd_level" : 991,
+ "temp_kf" : 0,
+ "humidity" : 57,
+ "pressure" : 1007,
+ "sea_level" : 1007,
+ "feels_like" : 1.43,
+ "temp_max" : 3.04
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 26,
+ "speed" : 1.71,
+ "gust" : 3.48
+ }
+ }, {
+ "dt" : 1732806000,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-28 15:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 3.55,
+ "temp_min" : 3.55,
+ "grnd_level" : 990,
+ "temp_kf" : 0,
+ "humidity" : 56,
+ "pressure" : 1007,
+ "sea_level" : 1007,
+ "feels_like" : 1.41,
+ "temp_max" : 3.55
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 345,
+ "speed" : 2.26,
+ "gust" : 3.77
+ }
+ }, {
+ "dt" : 1732816800,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-28 18:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 5.38,
+ "temp_min" : 5.38,
+ "grnd_level" : 989,
+ "temp_kf" : 0,
+ "humidity" : 49,
+ "pressure" : 1005,
+ "sea_level" : 1005,
+ "feels_like" : 2.39,
+ "temp_max" : 5.38
+ },
+ "clouds" : {
+ "all" : 85
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 317,
+ "speed" : 3.89,
+ "gust" : 5.52
+ }
+ }, {
+ "dt" : 1732827600,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-28 21:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 5.12,
+ "temp_min" : 5.12,
+ "grnd_level" : 991,
+ "temp_kf" : 0,
+ "humidity" : 46,
+ "pressure" : 1007,
+ "sea_level" : 1007,
+ "feels_like" : 1.99,
+ "temp_max" : 5.12
+ },
+ "clouds" : {
+ "all" : 99
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 310,
+ "speed" : 4.03,
+ "gust" : 6.04
+ }
+ }, {
+ "dt" : 1732838400,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-29 00:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 2.6,
+ "temp_min" : 2.6,
+ "grnd_level" : 993,
+ "temp_kf" : 0,
+ "humidity" : 58,
+ "pressure" : 1009,
+ "sea_level" : 1009,
+ "feels_like" : -1.46,
+ "temp_max" : 2.6
+ },
+ "clouds" : {
+ "all" : 85
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 285,
+ "speed" : 4.62,
+ "gust" : 8.29
+ }
+ }, {
+ "dt" : 1732849200,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-29 03:00:00",
+ "weather" : [ {
+ "icon" : "03n",
+ "description" : "scattered clouds",
+ "main" : "Clouds",
+ "id" : 802
+ } ],
+ "main" : {
+ "temp" : 1.53,
+ "temp_min" : 1.53,
+ "grnd_level" : 993,
+ "temp_kf" : 0,
+ "humidity" : 70,
+ "pressure" : 1010,
+ "sea_level" : 1010,
+ "feels_like" : -2.56,
+ "temp_max" : 1.53
+ },
+ "clouds" : {
+ "all" : 26
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 257,
+ "speed" : 4.24,
+ "gust" : 9.02
+ }
+ }, {
+ "dt" : 1732860000,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-29 06:00:00",
+ "weather" : [ {
+ "icon" : "02n",
+ "description" : "few clouds",
+ "main" : "Clouds",
+ "id" : 801
+ } ],
+ "main" : {
+ "temp" : 1.2,
+ "temp_min" : 1.2,
+ "grnd_level" : 993,
+ "temp_kf" : 0,
+ "humidity" : 64,
+ "pressure" : 1009,
+ "sea_level" : 1009,
+ "feels_like" : -3.13,
+ "temp_max" : 1.2
+ },
+ "clouds" : {
+ "all" : 22
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 253,
+ "speed" : 4.51,
+ "gust" : 8.84
+ }
+ }, {
+ "dt" : 1732870800,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-29 09:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : 0.67,
+ "temp_min" : 0.67,
+ "grnd_level" : 992,
+ "temp_kf" : 0,
+ "humidity" : 62,
+ "pressure" : 1009,
+ "sea_level" : 1009,
+ "feels_like" : -4.16,
+ "temp_max" : 0.67
+ },
+ "clouds" : {
+ "all" : 73
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 250,
+ "speed" : 5.14,
+ "gust" : 9.86
+ }
+ }, {
+ "dt" : 1732881600,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-29 12:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 0.43,
+ "temp_min" : 0.43,
+ "grnd_level" : 992,
+ "temp_kf" : 0,
+ "humidity" : 63,
+ "pressure" : 1009,
+ "sea_level" : 1009,
+ "feels_like" : -4.69,
+ "temp_max" : 0.43
+ },
+ "clouds" : {
+ "all" : 86
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 248,
+ "speed" : 5.58,
+ "gust" : 10.77
+ }
+ }, {
+ "dt" : 1732892400,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-29 15:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 0.86,
+ "temp_min" : 0.86,
+ "grnd_level" : 993,
+ "temp_kf" : 0,
+ "humidity" : 63,
+ "pressure" : 1009,
+ "sea_level" : 1009,
+ "feels_like" : -4.25,
+ "temp_max" : 0.86
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 250,
+ "speed" : 5.77,
+ "gust" : 10.56
+ }
+ }, {
+ "dt" : 1732903200,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-29 18:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 2.65,
+ "temp_min" : 2.65,
+ "grnd_level" : 992,
+ "temp_kf" : 0,
+ "humidity" : 46,
+ "pressure" : 1008,
+ "sea_level" : 1008,
+ "feels_like" : -2.09,
+ "temp_max" : 2.65
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 253,
+ "speed" : 6,
+ "gust" : 10
+ }
+ }, {
+ "dt" : 1732914000,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-29 21:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 1.98,
+ "temp_min" : 1.98,
+ "grnd_level" : 993,
+ "temp_kf" : 0,
+ "humidity" : 53,
+ "pressure" : 1009,
+ "sea_level" : 1009,
+ "feels_like" : -3.1,
+ "temp_max" : 1.98
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 255,
+ "speed" : 6.36,
+ "gust" : 11.97
+ }
+ }, {
+ "dt" : 1732924800,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-30 00:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 0.48,
+ "temp_min" : 0.48,
+ "grnd_level" : 994,
+ "temp_kf" : 0,
+ "humidity" : 64,
+ "pressure" : 1011,
+ "sea_level" : 1011,
+ "feels_like" : -5.01,
+ "temp_max" : 0.48
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 257,
+ "speed" : 6.35,
+ "gust" : 11.85
+ }
+ }, {
+ "dt" : 1732935600,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-30 03:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : -0.03,
+ "temp_min" : -0.03,
+ "grnd_level" : 994,
+ "temp_kf" : 0,
+ "humidity" : 60,
+ "pressure" : 1011,
+ "sea_level" : 1011,
+ "feels_like" : -5.62,
+ "temp_max" : -0.03
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 257,
+ "speed" : 6.26,
+ "gust" : 11.62
+ }
+ }, {
+ "dt" : 1732946400,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-30 06:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : -0.98,
+ "temp_min" : -0.98,
+ "grnd_level" : 995,
+ "temp_kf" : 0,
+ "humidity" : 64,
+ "pressure" : 1012,
+ "sea_level" : 1012,
+ "feels_like" : -6.77,
+ "temp_max" : -0.98
+ },
+ "clouds" : {
+ "all" : 78
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 252,
+ "speed" : 6.14,
+ "gust" : 12.41
+ }
+ }, {
+ "dt" : 1732957200,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-30 09:00:00",
+ "weather" : [ {
+ "icon" : "03n",
+ "description" : "scattered clouds",
+ "main" : "Clouds",
+ "id" : 802
+ } ],
+ "main" : {
+ "temp" : -0.99,
+ "temp_min" : -0.99,
+ "grnd_level" : 995,
+ "temp_kf" : 0,
+ "humidity" : 66,
+ "pressure" : 1012,
+ "sea_level" : 1012,
+ "feels_like" : -6.76,
+ "temp_max" : -0.99
+ },
+ "clouds" : {
+ "all" : 48
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 251,
+ "speed" : 6.11,
+ "gust" : 12.84
+ }
+ }, {
+ "dt" : 1732968000,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-30 12:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : -1.15,
+ "temp_min" : -1.15,
+ "grnd_level" : 997,
+ "temp_kf" : 0,
+ "humidity" : 67,
+ "pressure" : 1013,
+ "sea_level" : 1013,
+ "feels_like" : -7.06,
+ "temp_max" : -1.15
+ },
+ "clouds" : {
+ "all" : 55
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 251,
+ "speed" : 6.29,
+ "gust" : 12.65
+ }
+ }, {
+ "dt" : 1732978800,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-30 15:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : -0.33,
+ "temp_min" : -0.33,
+ "grnd_level" : 998,
+ "temp_kf" : 0,
+ "humidity" : 60,
+ "pressure" : 1014,
+ "sea_level" : 1014,
+ "feels_like" : -5.89,
+ "temp_max" : -0.33
+ },
+ "clouds" : {
+ "all" : 78
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 250,
+ "speed" : 6.05,
+ "gust" : 10.71
+ }
+ }, {
+ "dt" : 1732989600,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-30 18:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : 1.14,
+ "temp_min" : 1.14,
+ "grnd_level" : 997,
+ "temp_kf" : 0,
+ "humidity" : 47,
+ "pressure" : 1014,
+ "sea_level" : 1014,
+ "feels_like" : -3.93,
+ "temp_max" : 1.14
+ },
+ "clouds" : {
+ "all" : 78
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 252,
+ "speed" : 5.84,
+ "gust" : 8.39
+ }
+ }, {
+ "dt" : 1733000400,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-11-30 21:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 0.08,
+ "temp_min" : 0.08,
+ "grnd_level" : 998,
+ "temp_kf" : 0,
+ "humidity" : 55,
+ "pressure" : 1014,
+ "sea_level" : 1014,
+ "feels_like" : -5.22,
+ "temp_max" : 0.08
+ },
+ "clouds" : {
+ "all" : 99
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 248,
+ "speed" : 5.74,
+ "gust" : 8.98
+ }
+ }, {
+ "dt" : 1733011200,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-01 00:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : -0.77,
+ "temp_min" : -0.77,
+ "grnd_level" : 998,
+ "temp_kf" : 0,
+ "humidity" : 60,
+ "pressure" : 1015,
+ "sea_level" : 1015,
+ "feels_like" : -6.35,
+ "temp_max" : -0.77
+ },
+ "clouds" : {
+ "all" : 98
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 248,
+ "speed" : 5.84,
+ "gust" : 10.17
+ }
+ }, {
+ "dt" : 1733022000,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-01 03:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : -1.15,
+ "temp_min" : -1.15,
+ "grnd_level" : 998,
+ "temp_kf" : 0,
+ "humidity" : 65,
+ "pressure" : 1015,
+ "sea_level" : 1015,
+ "feels_like" : -6.62,
+ "temp_max" : -1.15
+ },
+ "clouds" : {
+ "all" : 99
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 243,
+ "speed" : 5.45,
+ "gust" : 10.23
+ }
+ }, {
+ "dt" : 1733032800,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-01 06:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : -1.37,
+ "temp_min" : -1.37,
+ "grnd_level" : 997,
+ "temp_kf" : 0,
+ "humidity" : 65,
+ "pressure" : 1014,
+ "sea_level" : 1014,
+ "feels_like" : -7.05,
+ "temp_max" : -1.37
+ },
+ "clouds" : {
+ "all" : 99
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 243,
+ "speed" : 5.73,
+ "gust" : 10.81
+ }
+ }, {
+ "dt" : 1733043600,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-01 09:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : -1.49,
+ "temp_min" : -1.49,
+ "grnd_level" : 997,
+ "temp_kf" : 0,
+ "humidity" : 64,
+ "pressure" : 1014,
+ "sea_level" : 1014,
+ "feels_like" : -7.13,
+ "temp_max" : -1.49
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 244,
+ "speed" : 5.61,
+ "gust" : 11.45
+ }
+ }, {
+ "dt" : 1733054400,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-01 12:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : -1.36,
+ "temp_min" : -1.36,
+ "grnd_level" : 997,
+ "temp_kf" : 0,
+ "humidity" : 67,
+ "pressure" : 1014,
+ "sea_level" : 1014,
+ "feels_like" : -7.16,
+ "temp_max" : -1.36
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 243,
+ "speed" : 5.97,
+ "gust" : 12.84
+ }
+ }, {
+ "dt" : 1733065200,
+ "pop" : 0.99,
+ "visibility" : 2980,
+ "dt_txt" : "2024-12-01 15:00:00",
+ "snow" : {
+ "3h" : 0.68
+ },
+ "weather" : [ {
+ "icon" : "13d",
+ "description" : "light snow",
+ "main" : "Snow",
+ "id" : 600
+ } ],
+ "main" : {
+ "temp" : -1.14,
+ "temp_min" : -1.14,
+ "grnd_level" : 998,
+ "temp_kf" : 0,
+ "humidity" : 85,
+ "pressure" : 1015,
+ "sea_level" : 1015,
+ "feels_like" : -6.49,
+ "temp_max" : -1.14
+ },
+ "clouds" : {
+ "all" : 100
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 250,
+ "speed" : 5.25,
+ "gust" : 11.59
+ }
+ }, {
+ "dt" : 1733076000,
+ "pop" : 0.92,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-01 18:00:00",
+ "snow" : {
+ "3h" : 0.21
+ },
+ "weather" : [ {
+ "icon" : "13d",
+ "description" : "light snow",
+ "main" : "Snow",
+ "id" : 600
+ } ],
+ "main" : {
+ "temp" : 1.58,
+ "temp_min" : 1.58,
+ "grnd_level" : 997,
+ "temp_kf" : 0,
+ "humidity" : 60,
+ "pressure" : 1014,
+ "sea_level" : 1014,
+ "feels_like" : -2.51,
+ "temp_max" : 1.58
+ },
+ "clouds" : {
+ "all" : 97
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 251,
+ "speed" : 4.26,
+ "gust" : 6.63
+ }
+ }, {
+ "dt" : 1733086800,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-01 21:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 1.32,
+ "temp_min" : 1.32,
+ "grnd_level" : 998,
+ "temp_kf" : 0,
+ "humidity" : 55,
+ "pressure" : 1015,
+ "sea_level" : 1015,
+ "feels_like" : -2.17,
+ "temp_max" : 1.32
+ },
+ "clouds" : {
+ "all" : 95
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 288,
+ "speed" : 3.31,
+ "gust" : 6.43
+ }
+ }, {
+ "dt" : 1733097600,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-02 00:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : -0.14,
+ "temp_min" : -0.14,
+ "grnd_level" : 1000,
+ "temp_kf" : 0,
+ "humidity" : 67,
+ "pressure" : 1016,
+ "sea_level" : 1016,
+ "feels_like" : -4.26,
+ "temp_max" : -0.14
+ },
+ "clouds" : {
+ "all" : 86
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 285,
+ "speed" : 3.73,
+ "gust" : 9.09
+ }
+ }, {
+ "dt" : 1733108400,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-02 03:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : -0.64,
+ "temp_min" : -0.64,
+ "grnd_level" : 1000,
+ "temp_kf" : 0,
+ "humidity" : 64,
+ "pressure" : 1017,
+ "sea_level" : 1017,
+ "feels_like" : -3.9,
+ "temp_max" : -0.64
+ },
+ "clouds" : {
+ "all" : 74
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 279,
+ "speed" : 2.62,
+ "gust" : 6.37
+ }
+ }, {
+ "dt" : 1733119200,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-02 06:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : -0.89,
+ "temp_min" : -0.89,
+ "grnd_level" : 1000,
+ "temp_kf" : 0,
+ "humidity" : 70,
+ "pressure" : 1017,
+ "sea_level" : 1017,
+ "feels_like" : -4.07,
+ "temp_max" : -0.89
+ },
+ "clouds" : {
+ "all" : 75
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 296,
+ "speed" : 2.49,
+ "gust" : 4.68
+ }
+ }, {
+ "dt" : 1733130000,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-02 09:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : -0.77,
+ "temp_min" : -0.77,
+ "grnd_level" : 1001,
+ "temp_kf" : 0,
+ "humidity" : 68,
+ "pressure" : 1018,
+ "sea_level" : 1018,
+ "feels_like" : -3.96,
+ "temp_max" : -0.77
+ },
+ "clouds" : {
+ "all" : 78
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 291,
+ "speed" : 2.52,
+ "gust" : 4.73
+ }
+ }, {
+ "dt" : 1733140800,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-02 12:00:00",
+ "weather" : [ {
+ "icon" : "04n",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : -0.97,
+ "temp_min" : -0.97,
+ "grnd_level" : 1001,
+ "temp_kf" : 0,
+ "humidity" : 71,
+ "pressure" : 1018,
+ "sea_level" : 1018,
+ "feels_like" : -4.14,
+ "temp_max" : -0.97
+ },
+ "clouds" : {
+ "all" : 82
+ },
+ "sys" : {
+ "pod" : "n"
+ },
+ "wind" : {
+ "deg" : 321,
+ "speed" : 2.47,
+ "gust" : 6.01
+ }
+ }, {
+ "dt" : 1733151600,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-02 15:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : -0.1,
+ "temp_min" : -0.1,
+ "grnd_level" : 1003,
+ "temp_kf" : 0,
+ "humidity" : 69,
+ "pressure" : 1020,
+ "sea_level" : 1020,
+ "feels_like" : -3,
+ "temp_max" : -0.1
+ },
+ "clouds" : {
+ "all" : 54
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 342,
+ "speed" : 2.37,
+ "gust" : 4.47
+ }
+ }, {
+ "dt" : 1733162400,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-02 18:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "broken clouds",
+ "main" : "Clouds",
+ "id" : 803
+ } ],
+ "main" : {
+ "temp" : 1.01,
+ "temp_min" : 1.01,
+ "grnd_level" : 1003,
+ "temp_kf" : 0,
+ "humidity" : 59,
+ "pressure" : 1020,
+ "sea_level" : 1020,
+ "feels_like" : -1.04,
+ "temp_max" : 1.01
+ },
+ "clouds" : {
+ "all" : 60
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 306,
+ "speed" : 1.81,
+ "gust" : 2.47
+ }
+ }, {
+ "dt" : 1733173200,
+ "pop" : 0,
+ "visibility" : 10000,
+ "dt_txt" : "2024-12-02 21:00:00",
+ "weather" : [ {
+ "icon" : "04d",
+ "description" : "overcast clouds",
+ "main" : "Clouds",
+ "id" : 804
+ } ],
+ "main" : {
+ "temp" : 0.9,
+ "temp_min" : 0.9,
+ "grnd_level" : 1004,
+ "temp_kf" : 0,
+ "humidity" : 66,
+ "pressure" : 1021,
+ "sea_level" : 1021,
+ "feels_like" : -1.69,
+ "temp_max" : 0.9
+ },
+ "clouds" : {
+ "all" : 92
+ },
+ "sys" : {
+ "pod" : "d"
+ },
+ "wind" : {
+ "deg" : 309,
+ "speed" : 2.25,
+ "gust" : 4.02
+ }
+ } ]
+}
\ No newline at end of file
diff --git a/src/test/java/app/MainNoteApplicationTest.java b/src/test/java/app/MainNoteApplicationTest.java
deleted file mode 100644
index 025d970e2..000000000
--- a/src/test/java/app/MainNoteApplicationTest.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package app;
-
-import entity.User;
-import org.junit.Before;
-import org.junit.Test;
-import use_case.note.NoteDataAccessInterface;
-
-import javax.swing.*;
-import java.awt.*;
-
-import static java.lang.Thread.sleep;
-import static org.junit.Assert.*;
-
-public class MainNoteApplicationTest {
-
- private JFrame app;
-
- @Before
- public void setUp() {
-
- // create the data access and inject it into our builder!
- final NoteDataAccessInterface noteDataAccess = new NoteDataAccessInterface() {
-
- private String note = "test";
-
- @Override
- public String saveNote(User user, String note) {
- this.note = note;
- return note;
- }
-
- @Override
- public String loadNote(User user) {
- return note;
- }
- };
-
- final NoteAppBuilder builder = new NoteAppBuilder();
- app = builder.addNoteDAO(noteDataAccess)
- .addNoteView()
- .addNoteUseCase().build();
-
- app.setVisible(true);
-
- }
-
- /**
- * This is an example of an end-to-end test with a mocked database.
- * The code creates the application and directly tests to see that the GUI
- * is updated as expected when the buttons and UI elements are interacted with.
- *
- * You can run the test to visually see what happens.
- */
- @Test
- public void testEndToEnd() {
-
- Component[] components = ((JPanel)app.getRootPane().getContentPane().getComponents()[0]).getComponents();
- JTextArea textArea = null;
- for (Component component : components) {
- if (component instanceof JTextArea) {
- textArea = (JTextArea) component;
- assertEquals("test", textArea.getText());
-
- }
- }
-
- textArea.setText("test test");
-
-
- JButton save = null;
- JButton load = null;
- for (Component component : components) {
- if (component instanceof JPanel) {
- for (Component c : ((JPanel) component).getComponents()) {
- if (c instanceof JButton) {
- if (save != null) {
- load = (JButton) c;
- }
- else {
- save = (JButton) c;
- }
- }
- }
- }
- }
-
- save.doClick();
- assertEquals("test test", textArea.getText());
- textArea.setText("");
-
- System.out.println("cleared text; about to refresh...");
- // pause execution for a bit so we can visually see the changes on the screen
- try {
- sleep(1500);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
-
- load.doClick();
- assertEquals("test test", textArea.getText());
-
- System.out.println("after refresh!");
-
- // pause execution for a bit so we can visually see the changes on the screen
- try {
- sleep(1500);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
-
- }
-}
\ No newline at end of file
diff --git a/src/test/java/use_case/note/CompareCitiesInteractorTest.java b/src/test/java/use_case/note/CompareCitiesInteractorTest.java
new file mode 100644
index 000000000..edbc77c05
--- /dev/null
+++ b/src/test/java/use_case/note/CompareCitiesInteractorTest.java
@@ -0,0 +1,139 @@
+package use_case.note;
+
+import data_access.InMemoryUserDataAccessObject;
+import data_access.WeatherDataAccessObject;
+import entity.Weather;
+import interface_adapter.CompareCities.CompareCitiesPresenter;
+import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import use_case.note.CompareCities.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class CompareCitiesInteractorTest {
+ private CompareCitiesOutputBoundary presenter;
+ private CompareCitiesDataAccessInterface compareCitiesDataAccessInterface;
+
+ @Test
+ public void successTest() {
+ // this is the mock input data.
+ final CompareCitiesInputData inputData = new CompareCitiesInputData("Toronto", "Tokyo");
+
+ compareCitiesDataAccessInterface = new InMemoryUserDataAccessObject();
+
+ presenter = new CompareCitiesOutputBoundary() {
+ // make a presenter
+ @Override
+ public void prepareSuccessView(CompareCitiesOutPutData outputData) {
+ Assertions.assertEquals("Toronto", outputData.getFirstCityname());
+ Assertions.assertEquals(1.0, outputData.getSecondWeather().getTemperature());
+ Assertions.assertEquals("Tokyo", outputData.getSecondCityname());
+ Assertions.assertEquals(2, outputData.getSecondWeather().getHumidity());
+ }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+ Assertions.fail("city not found");
+ }
+ };
+
+ CompareCitiesInputBoundary interactor = new CompareCitiesInteractor(compareCitiesDataAccessInterface, presenter);
+ interactor.execute(inputData);
+ }
+ @Test
+ public void successexecuteTest () {
+ CompareCitiesInputData inputdata = new CompareCitiesInputData("Toronto", "Tokyo");
+ compareCitiesDataAccessInterface = new InMemoryUserDataAccessObject();
+ String firstcityName = "Toronto";
+ Double firsttemperature = 10.5;
+ String firstsky = "rain";
+ String firstdescription = "first description";
+ Double firstwindSpeed = 0.0;
+ int firsthumidity = 1;
+ int firstvisibility = 2;
+ Double firstlon = 3.0;
+ Double firstlat = 4.0;
+ String firstalertDescription = "No alerts";
+
+ Weather firstweather = new Weather(firstcityName, firsttemperature, firstsky,firstdescription, firstwindSpeed, firsthumidity, firstvisibility, firstlon, firstlat, firstalertDescription);
+ compareCitiesDataAccessInterface.saveWeatherinfor(firstweather);
+ String secondcityName = "Tokyo";
+ Double secondtemperature = 1.0;
+ String secondsky = "cloud";
+ String seconddescription = "second description";
+ Double secondwindSpeed = 2.0;
+ int secondhumidity = 2;
+ int secondvisibility = 1000;
+ Double secondlon = 20.0;
+ Double secondlat = 25.0;
+ String secondalertDescription = "Alerts";
+ Weather secondweather = new Weather(secondcityName, secondtemperature, secondsky, seconddescription, secondwindSpeed, secondhumidity, secondvisibility, secondlon, secondlat, secondalertDescription);
+ compareCitiesDataAccessInterface.saveWeatherinfor(secondweather);
+ }
+
+ @Test
+ public void testExecute_SameCityComparison() {
+ // Arrange
+ CompareCitiesInputData inputData = new CompareCitiesInputData("Toronto", "Toronto");
+
+ // Act
+ CompareCitiesInputBoundary interactor = new CompareCitiesInteractor(compareCitiesDataAccessInterface, presenter);
+ interactor.execute(inputData);
+
+ // Assert
+ presenter.prepareFailView("Cannot compare the same city");
+ }
+}
+
+//
+//// Weather weather1 = compareCitiesDataAccessInterface.getWeather(inputData.getFirstcityname());
+//// compareCitiesDataAccessInterface.saveWeatherinfor(weather1);
+//// Weather weather2 = compareCitiesDataAccessInterface.getWeather(inputData.getSecondcityname());
+//// compareCitiesDataAccessInterface.saveWeatherinfor(weather2);
+// }
+//
+// @Override
+// public boolean isCityexist(String cityname) {
+// return true;
+// }
+//
+// @Override
+// public boolean isCityExist(String cityname) {
+// return false;
+// }
+// } {
+//
+//
+// @Override
+// public String saveWeatherinfor(Weather weather) {
+// return "";
+// }
+//
+//
+// @Override
+// public String loadWeather(Weather weather) {
+// return "test";
+// }
+// };
+//
+// CompareCitiesOutputBoundary compareCitiesOB = new CompareCitiesOutputBoundary() {
+// @Override
+// public void prepareSuccessView(String message) {
+// assertEquals("test", message);
+// }
+//
+// @Override
+// public void prepareFailView(String errorMessage) {
+// fail(errorMessage);
+// }
+// };
+//
+// CompareCitiesInteractor compareCitiesInteractor = new CompareCitiesInteractor(compareCitiesDataAccessInterface, compareCitiesOB);
+// CompareCitiesInputData inputData = new CompareCitiesInputData("City1", "City2");
+//
+// compareCitiesInteractor.execute(inputData);
+//
+//
+// }
+//}
\ No newline at end of file
diff --git a/src/test/java/use_case/note/CompareCitiesInteractorTest2.java b/src/test/java/use_case/note/CompareCitiesInteractorTest2.java
new file mode 100644
index 000000000..617622a28
--- /dev/null
+++ b/src/test/java/use_case/note/CompareCitiesInteractorTest2.java
@@ -0,0 +1,177 @@
+package use_case.note;
+
+import data_access.InMemoryUserDataAccessObject; // Replace with the correct package if different
+import data_access.WeatherDataAccessObject;
+import entity.Weather;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.*;
+import use_case.note.CompareCities.*;
+
+import java.io.IOException;
+
+class CompareCitiesInteractorTest2 {
+
+ @Test
+ public void testSuccessComparison() {
+ // Arrange
+ CompareCitiesDataAccessInterface dao = new InMemoryUserDataAccessObject();
+
+ // Add mock weather data directly into the DAO
+ dao.saveWeatherinfor(new Weather("Toronto", 10.5, "Clear", "Sunny", 5.0, 50, 1000, -79.38, 43.65, "No alerts"));
+ dao.saveWeatherinfor(new Weather("Tokyo", 15.0, "Cloudy", "Overcast", 3.0, 70, 800, 139.69, 35.69, "No alerts"));
+
+ CompareCitiesOutputBoundary presenter = new CompareCitiesOutputBoundary() {
+ @Override
+ public void prepareSuccessView(CompareCitiesOutPutData outputData) {
+ assertEquals("Toronto", outputData.getFirstCityname());
+ assertEquals(10.5, outputData.getFirstWeather().getTemperature(), 0.01);
+ assertEquals("Tokyo", outputData.getSecondCityname());
+ assertEquals(1.0, outputData.getSecondWeather().getTemperature(), 0.01);
+ }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+ fail("Unexpected failure: " + errorMessage);
+ }
+ };
+
+ CompareCitiesInteractor interactor = new CompareCitiesInteractor(dao, presenter);
+ CompareCitiesInputData inputData = new CompareCitiesInputData("Toronto", "Tokyo");
+
+ // Act
+ interactor.execute(inputData);
+ }
+
+ @Test
+ public void testSameCityComparison() {
+ // Arrange
+ CompareCitiesDataAccessInterface dao = new InMemoryUserDataAccessObject();
+
+ CompareCitiesOutputBoundary presenter = new CompareCitiesOutputBoundary() {
+ @Override
+ public void prepareSuccessView(CompareCitiesOutPutData outputData) {
+ fail("Comparison of the same city should fail.");
+ }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+ assertEquals("Cannot compare the same city", errorMessage);
+ }
+ };
+
+ CompareCitiesInteractor interactor = new CompareCitiesInteractor(dao, presenter);
+ CompareCitiesInputData inputData = new CompareCitiesInputData("Toronto", "Toronto");
+
+ // Act
+ interactor.execute(inputData);
+ }
+
+ @Test
+ public void testCityNotFound1() {
+ // Arrange
+ CompareCitiesDataAccessInterface dao = new InMemoryUserDataAccessObject();
+
+ CompareCitiesOutputBoundary presenter = new CompareCitiesOutputBoundary() {
+ @Override
+ public void prepareSuccessView(CompareCitiesOutPutData outputData) {
+ fail("City not found should fail.");
+ }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+ assertEquals("city not found", errorMessage);
+ }
+ };
+
+
+ CompareCitiesInteractor interactor = new CompareCitiesInteractor(dao, presenter);
+ CompareCitiesInputData inputData = new CompareCitiesInputData("NonExistentCity", "Tokyo");
+
+ // Act
+ try {
+ interactor.execute(inputData);
+ }
+ catch (RuntimeException e) {
+ assertFalse(e.getCause() instanceof IOException);
+ assertEquals("Failed to fetch weather data", e.getMessage());
+ }
+ }
+ @Test
+ public void testCityNotFound2() {
+ // Arrange
+ CompareCitiesDataAccessInterface dao = new InMemoryUserDataAccessObject();
+
+ CompareCitiesOutputBoundary presenter = new CompareCitiesOutputBoundary() {
+ @Override
+ public void prepareSuccessView(CompareCitiesOutPutData outputData) {
+ fail("City not found should fail.");
+ }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+ assertEquals("city not found", errorMessage);
+ }
+ };
+
+
+ CompareCitiesInteractor interactor = new CompareCitiesInteractor(dao, presenter);
+ CompareCitiesInputData inputData = new CompareCitiesInputData("Toronto", "randomcity");
+
+ // Act
+ try {
+ interactor.execute(inputData);
+ }
+ catch (RuntimeException e) {
+ assertFalse(e.getCause() instanceof IOException);
+ assertEquals("Failed to fetch weather data", e.getMessage());
+ }
+
+ }
+
+ @Test
+ public void testIOExceptionHandlingForBothCities() {
+ // Custom DAO that throws IOException for both cities
+ CompareCitiesDataAccessInterface dao = new InMemoryUserDataAccessObject() {
+ @Override
+ public Weather getWeather(String cityName) throws IOException {
+ throw new IOException("Network error while fetching weather data for " + cityName);
+ }
+ };
+
+ CompareCitiesOutputBoundary presenter = new CompareCitiesOutputBoundary() {
+ @Override
+ public void prepareSuccessView(CompareCitiesOutPutData outputData) {
+ fail("IOException should result in failure, not success.");
+ }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+ // Check if the error message is the same for the first city
+ if (errorMessage.contains("Toronto")) {
+ assertTrue(errorMessage.contains("Network error while fetching weather data for Toronto"));
+ }
+ // Check if the error message is the same for the second city
+ else if (errorMessage.contains("Tokyo")) {
+ assertTrue(errorMessage.contains("Network error while fetching weather data for Tokyo"));
+ } else {
+ fail("Unexpected error message: " + errorMessage);
+ }
+ }
+ };
+
+ CompareCitiesInteractor interactor = new CompareCitiesInteractor(dao, presenter);
+ CompareCitiesInputData inputData = new CompareCitiesInputData("Toronto", "Tokyo");
+
+ // Act
+ try {
+ interactor.execute(inputData);
+ }
+ catch (RuntimeException e) {
+ assertFalse(e.getCause() instanceof IOException);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/use_case/note/ConvertInteractorTest.java b/src/test/java/use_case/note/ConvertInteractorTest.java
new file mode 100644
index 000000000..b6d7d4a0b
--- /dev/null
+++ b/src/test/java/use_case/note/ConvertInteractorTest.java
@@ -0,0 +1,118 @@
+package use_case.note;
+
+import data_access.WeatherDataAccessObject;
+import entity.Weather;
+import org.junit.Test;
+import use_case.note.convert_farenheit.ConvertFarenheitInputData;
+import use_case.note.convert_farenheit.ConvertFarenheitOutputBoundary;
+import use_case.note.convert_farenheit.ConvertFarenheitOutputData;
+import use_case.note.convert_farenheit.ConvertInteractor;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+public class ConvertInteractorTest {
+
+ /**
+ * Checks Lines 1 - 14.
+ */
+ @Test
+ public void TestIfWeatherDNE() {
+ WeatherDataAccessInterface Dao = new WeatherDataAccessInterface() {
+ @Override
+ public Weather getWeather(String city) throws IOException {
+
+ return new WeatherDataAccessObject().getWeather(city);
+ }
+ };
+ ConvertFarenheitOutputBoundary boundary = new ConvertFarenheitOutputBoundary() {
+ @Override
+ public void prepareFailView(String errorMessage) {
+ assertEquals(errorMessage,"UNABLE TO CONVERT");
+ }
+
+ @Override
+ public void prepareSuccessView(ConvertFarenheitOutputData outputData) {
+ fail("this is unexpected");
+ }
+ };
+ Weather weather = Dao.getWeather("trn");
+
+ ConvertFarenheitInputData inputData = new ConvertFarenheitInputData(weather);
+
+ ConvertInteractor interactor = new ConvertInteractor(boundary);
+ interactor.executeConvert(inputData);
+ }
+
+ /**
+ * Convert to Fahrenheit.
+ * Checks Lines 28 - 36 and lines 15 - 22.
+ */
+ @Test
+ public void TestIfMetricConversion() {
+ WeatherDataAccessInterface Dao = new WeatherDataAccessInterface() {
+ @Override
+ public Weather getWeather(String city) throws IOException {
+ return WeatherDataAccessObject.getWeather(city);
+ }
+ };
+ ConvertFarenheitOutputBoundary boundary = new ConvertFarenheitOutputBoundary() {
+ @Override
+ public void prepareFailView(String errorMessage) {
+ fail(errorMessage);
+ }
+
+ @Override
+ public void prepareSuccessView(ConvertFarenheitOutputData outputData) {
+ assertEquals(false, outputData.getWeather().isMetric());
+
+ }
+ };
+
+ Weather weather = Dao.getWeather("Toronto");
+
+ ConvertFarenheitInputData inputData = new ConvertFarenheitInputData(weather);
+
+ ConvertInteractor interactor = new ConvertInteractor(boundary);
+ interactor.executeConvert(inputData);
+
+
+ }
+
+ /**
+ * Convert to Metric.
+ * Checks Lines 37 - end and lines 15 - 22.
+ */
+ @Test
+ public void TestIfFahrenheitConversion() {
+ WeatherDataAccessInterface Dao = new WeatherDataAccessInterface() {
+ @Override
+ public Weather getWeather(String city) throws IOException {
+ return WeatherDataAccessObject.getWeather(city);
+ }
+ };
+ ConvertFarenheitOutputBoundary boundary = new ConvertFarenheitOutputBoundary() {
+ @Override
+ public void prepareFailView(String errorMessage) {
+ fail(errorMessage);
+ }
+
+ @Override
+ public void prepareSuccessView(ConvertFarenheitOutputData outputData) {
+ assertTrue(outputData.getWeather().isMetric());
+
+ }
+ };
+
+ Weather weather = Dao.getWeather("Toronto");
+ weather.setMetric(false);
+
+ ConvertFarenheitInputData inputData = new ConvertFarenheitInputData(weather);
+
+ ConvertInteractor interactor = new ConvertInteractor(boundary);
+ interactor.executeConvert(inputData);
+
+ }
+}
diff --git a/src/test/java/use_case/note/NearbyListInteractorTest.java b/src/test/java/use_case/note/NearbyListInteractorTest.java
new file mode 100644
index 000000000..48e4a663f
--- /dev/null
+++ b/src/test/java/use_case/note/NearbyListInteractorTest.java
@@ -0,0 +1,36 @@
+package use_case.note;
+
+import org.junit.Test;
+import use_case.note.nearby_list.*;
+
+import java.io.IOException;
+import java.util.List;
+
+public class NearbyListInteractorTest {
+
+ @Test
+ public void testOutput() {
+ NearbyCitiesAccessInterface cityDAO = new NearbyCitiesAccessInterface() {
+ @Override
+ public List getNearbyCities(double latitude, double longitude) throws IOException {
+ return List.of();
+ }
+ };
+
+ NearbyListOutputBoundary cityOB = new NearbyListOutputBoundary() {
+ @Override
+ public void presentSuccessView(NearbyListOutputData nearbyListOutputData) {
+
+ }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+
+ }
+ };
+
+ NearbyListInteractor cityInteractor = new NearbyListInteractor(cityOB, cityDAO);
+ NearbyListInputData input = new NearbyListInputData(0.0, 0.0);
+ cityInteractor.execute(input);
+ }
+}
diff --git a/src/test/java/use_case/note/NoteInteractorTest.java b/src/test/java/use_case/note/NoteInteractorTest.java
deleted file mode 100644
index a3ed466b6..000000000
--- a/src/test/java/use_case/note/NoteInteractorTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package use_case.note;
-
-import entity.User;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class NoteInteractorTest {
-
- @Test
- public void testExecuteRefreshSuccess() {
-
- NoteDataAccessInterface noteDAO = new NoteDataAccessInterface() {
-
-
- @Override
- public String saveNote(User user, String note) {
- return "";
- }
-
-
- @Override
- public String loadNote(User user) {
- return "test";
- }
- };
-
- NoteOutputBoundary noteOB = new NoteOutputBoundary() {
- @Override
- public void prepareSuccessView(String message) {
- assertEquals("test", message);
- }
-
- @Override
- public void prepareFailView(String errorMessage) {
- fail(errorMessage);
- }
- };
-
- NoteInteractor noteInteractor = new NoteInteractor(noteDAO, noteOB);
-
- noteInteractor.executeRefresh();
-
-
- }
-}
\ No newline at end of file
diff --git a/src/test/java/use_case/note/SearchResultInteractorTest.java b/src/test/java/use_case/note/SearchResultInteractorTest.java
new file mode 100644
index 000000000..1450f0189
--- /dev/null
+++ b/src/test/java/use_case/note/SearchResultInteractorTest.java
@@ -0,0 +1,115 @@
+package use_case.note;
+
+import entity.Weather;
+import org.junit.Before;
+import org.junit.Test;
+import use_case.note.search_result.*;
+
+import java.io.IOException;
+
+import static junit.framework.TestCase.assertNull;
+import static org.junit.Assert.*;
+
+public class SearchResultInteractorTest {
+ private SearchResultInputBoundary inputBoundary;
+ private SearchResultOutputBoundary outputBoundary;
+ private WeatherDataAccessInterface weatherDataAccess;
+ private HistoricalWeatherDataAccessInterface historicalWeatherDataAccess;
+ private SearchResultInputData inputData;
+
+ @Before
+ public void setUp() {
+ historicalWeatherDataAccess = new HistoricalWeatherDataAccessInterface() {
+ @Override
+ public void saveWeather(Weather weather, String timestamp) throws IOException {
+ // No-op for testing
+ }
+
+ @Override
+ public Weather getWeather(String city, String timestamp) throws IOException {
+ return createMockWeather();
+ }
+ };
+
+ weatherDataAccess = new WeatherDataAccessInterface() {
+ @Override
+ public Weather getWeather(String city) throws IOException {
+ return createMockWeather();
+ }
+ };
+
+ outputBoundary = new SearchResultOutputBoundary() {
+ @Override
+ public void presentSuccessView(SearchResultOutputData outputData) {
+ Weather expectedWeather = createMockWeather();
+ assertNotEquals(expectedWeather, outputData.getWeather());
+ assertTrue(!outputData.isUseCaseFailed());
+ }
+
+ @Override
+ public void presentFailView(String errorMessage) {
+ assertNull(errorMessage);
+ }
+ };
+
+ inputData = new SearchResultInputData("Toronto", "2023-11-27");
+ }
+
+ @Test
+ public void testExecuteSuccess() throws IOException {
+ SearchResultInteractor interactor = new SearchResultInteractor(outputBoundary, weatherDataAccess, historicalWeatherDataAccess);
+ interactor.execute(inputData);
+ }
+
+ @Test
+ public void testExecuteFailure() throws IOException {
+ historicalWeatherDataAccess = new HistoricalWeatherDataAccessInterface() {
+
+ @Override
+ public void saveWeather(Weather weather, String timestamp) throws IOException {
+ // No-op for testing
+ throw new IOException("Failed to retrieve weather data");
+ }
+
+ @Override
+ public Weather getWeather(String city, String timestamp) throws IOException {
+ throw new IOException("Failed to retrieve weather data");
+ }
+ };
+
+ outputBoundary = new SearchResultOutputBoundary() {
+ @Override
+ public void presentSuccessView(SearchResultOutputData outputData) {
+ fail("Expected failure, but success view was presented");
+ }
+
+ @Override
+ public void presentFailView(String errorMessage) {
+ assertNotNull(errorMessage);
+ assertEquals("Failed to retrieve weather data: Failed to retrieve weather data", errorMessage);
+ }
+ };
+
+ SearchResultInteractor interactor = new SearchResultInteractor(outputBoundary,
+ weatherDataAccess, historicalWeatherDataAccess);
+ try {
+ interactor.execute(inputData);
+ } catch (RuntimeException e) {
+ assertEquals("Failed to retrieve weather data", e.getMessage());
+ }
+ }
+
+ private Weather createMockWeather() {
+ String cityName = "Toronto";
+ Double temperature = 10.0;
+ String weather1 = "Cloudy";
+ String description = "Cloudy with a chance of meatballs";
+ Double windSpeed = 10.0;
+ int humidity = 10;
+ int visibility = 10;
+ Double lon = 10.0;
+ Double lat = 10.0;
+ String alertDescription = "No alerts";
+ return new Weather(cityName, temperature, weather1, description, windSpeed, humidity, visibility, lon, lat, alertDescription);
+ }
+}
diff --git a/src/test/java/use_case/note/SearchReturnInteractorTest.java b/src/test/java/use_case/note/SearchReturnInteractorTest.java
new file mode 100644
index 000000000..d3db9f529
--- /dev/null
+++ b/src/test/java/use_case/note/SearchReturnInteractorTest.java
@@ -0,0 +1,137 @@
+package use_case.note;
+
+import org.junit.Before;
+import org.junit.Test;
+import use_case.note.search_return.*;
+import entity.Weather;
+
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+public class SearchReturnInteractorTest {
+
+ private SearchReturnOutputBoundary outputBoundary;
+ private WeatherDataAccessInterface weatherDataAccess;
+ private HistoricalWeatherDataAccessInterface historicalWeatherDataAccess;
+
+ private SearchReturnInteractor interactor;
+
+ @Before
+ public void setUp() {
+ outputBoundary = new SearchReturnOutputBoundary() {
+ @Override
+ public void presentSuccessView(SearchReturnOutputData searchReturnOutputData) {
+ // Success handling implementation
+ }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+ // Failure handling implementation
+ assertNotNull(errorMessage);
+ assertTrue(errorMessage.startsWith("Failed to retrieve weather data"));
+ }
+ };
+
+ weatherDataAccess = new WeatherDataAccessInterface() {
+ @Override
+ public Weather getWeather(String city) throws IOException {
+ if (city.equals("Toronto")) {
+ String cityName = "Toronto";
+ Double temperature = 10.0;
+ String weather1 = "Cloudy";
+ String description = "Cloudy with a chance of meatballs";
+ Double windSpeed = 10.0;
+ int humidity = 10;
+ int visibility = 10;
+ Double lon = 10.0;
+ Double lat = 10.0;
+ String alertDescription = "No alerts";
+ return new Weather(cityName, temperature, weather1, description, windSpeed, humidity, visibility, lon, lat, alertDescription);
+ }
+ throw new IOException("Failed to fetch weather data");
+ }
+ };
+
+ historicalWeatherDataAccess = new HistoricalWeatherDataAccessInterface() {
+ @Override
+ public void saveWeather(Weather weather, String timestamp) {
+ // Save weather data implementation
+ }
+
+ @Override
+ public Weather getWeather(String city, String timestamp) throws IOException {
+ return null;
+ }
+ };
+
+ interactor = new SearchReturnInteractor(outputBoundary, weatherDataAccess, historicalWeatherDataAccess);
+ }
+
+ @Test
+ public void testExecute_success() {
+ // Arrange
+ String city = "Toronto";
+ SearchReturnInputData inputData = new SearchReturnInputData(city);
+
+ // Act
+ interactor.execute(inputData);
+
+ // Assert
+ String cityName = "Toronto";
+ Double temperature = 10.0;
+ String weather1 = "Cloudy";
+ String description = "Cloudy with a chance of meatballs";
+ Double windSpeed = 10.0;
+ int humidity = 10;
+ int visibility = 10;
+ Double lon = 10.0;
+ Double lat = 10.0;
+ String alertDescription = "No alerts";
+ Weather weather = new Weather(cityName, temperature, weather1, description, windSpeed, humidity, visibility, lon, lat, alertDescription);
+ SearchReturnOutputData outputData = new SearchReturnOutputData(weather, false);
+ assertEquals(10.0, outputData.getWeather().getTemperature(), 0.0);
+ assertEquals("Cloudy with a chance of meatballs", outputData.getWeather().getDescription());
+ }
+
+ @Test
+ public void testExecute_failure() {
+ // Arrange
+ historicalWeatherDataAccess = new HistoricalWeatherDataAccessInterface() {
+ @Override
+ public void saveWeather(Weather weather, String timestamp) throws IOException {
+ throw new IOException("Simulated IO Exception");
+ }
+
+ @Override
+ public Weather getWeather(String city, String timestamp) throws IOException {
+ return null;
+ }
+ };
+ outputBoundary = new SearchReturnOutputBoundary() {
+ @Override
+ public void presentSuccessView(SearchReturnOutputData searchReturnOutputData) {
+ fail("Expected failure, but success view was presented");
+ }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+ // Failure handling implementation
+ assertNotNull(errorMessage);
+ assertTrue(errorMessage.startsWith("Failed to retrieve weather data"));
+ }
+ };
+ interactor = new SearchReturnInteractor(outputBoundary, weatherDataAccess, historicalWeatherDataAccess);
+ String city = "Unknown City";
+ SearchReturnInputData inputData = new SearchReturnInputData(city);
+
+ // Act
+ try {
+ interactor.execute(inputData);
+ } catch (RuntimeException e) {
+ assertFalse(e.getCause() instanceof IOException);
+ assertEquals("Failed to fetch weather data", e.getMessage());
+ }
+
+ }
+}
diff --git a/src/test/java/use_case/note/alert_pop/AlertPopInteractorTest.java b/src/test/java/use_case/note/alert_pop/AlertPopInteractorTest.java
new file mode 100644
index 000000000..01b33a520
--- /dev/null
+++ b/src/test/java/use_case/note/alert_pop/AlertPopInteractorTest.java
@@ -0,0 +1,36 @@
+package use_case.note.alert_pop;
+
+import data_access.TestDataAccessObject;
+import data_access.WeatherDataAccessObject;
+import entity.Weather;
+import interface_adapter.alert_pop.AlertPopPresenter;
+import interface_adapter.weather.WeatherViewModel;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import use_case.note.WeatherDataAccessInterface;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class AlertPopInteractorTest {
+
+ private TestDataAccessObject testDataAccess;
+ private WeatherViewModel weatherViewModel;
+ private AlertPopOutputBoundary alertPopOutputBoundary;
+ private AlertPopInputData alertPopInputData;
+
+ @BeforeEach
+ void init() {
+ testDataAccess = new TestDataAccessObject();
+ weatherViewModel = new WeatherViewModel();
+ alertPopOutputBoundary = new AlertPopPresenter(weatherViewModel);
+ }
+
+ @Test
+ void successTest() {
+ alertPopInputData = new AlertPopInputData("Toronto");
+ AlertPopInteractor interactor = new AlertPopInteractor(testDataAccess, alertPopOutputBoundary);
+ Weather weather = new Weather("Toronto", 12f, "Sunny", "broken clouds",
+ 21f, 69, 24, 79, 44, "no weather alert");
+ testDataAccess.saveWeatherinfor(weather);
+ }
+}
\ No newline at end of file