Question
City Data Download: cities.csv This project includes GeoLite data created by MaxMind, available from http://www.maxmind.com In this project we will be taking advantage of city
City Data
Download: cities.csv This project includes GeoLite data created by MaxMind, available from http://www.maxmind.com
In this project we will be taking advantage of city location and population data from MaxMind which provides location and population data for thousands of cities from hundreds of countries. The cities.csv file contains a subset of the data set that has been modified to be suitable for this project (Contains only cities with population data; accent names removed; header line removed) in the csv format "Country,City,Region,Population,Latitude,Longitude". This is the exact file that will used while testing your project in AutoLab. Please visit the source website for more information about this data as well as a download of the unmodified data set.
Map Tile API
Documentation: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames API root: "http://a.tile.openstreetmap.org/" Sample usage: http://a.tile.openstreetmap.org/6/17/23.png
To get map images in our program we will use the freely available open street map tile server. This API serves partial images of the world map called tiles. A specific tile is requested by specifying the x and y locations as well as the zoom level of the desired tile where x and y are computed using a Mercator projection. Review the documentation for more details about requesting tiles and computing the projection. Note that the site provides example code that you cannot use since you did not write it and it's not provided by the course. You can however use these examples as a reference and implement them in your own way with your own code.
Objective 1: GUI Setup
As with the rhymes project, we will begin by setting up the layout for the project. In fact, the structure of this project is similar to the rhymes project throughout the 5 objectives.
Tasks
Create a new project in Eclipse
Create a new package in the src folder named "maps"
In the maps package, create a new package named "cities"
In the cities package, create a new class named "Cities" with a public constructor that takes a String as a parameter. The body of the constructor can be empty for now. We will add methods and instance variables to this class in later objectives
In the maps package, create a new class named "MapGUI" with a public constructor that takes an instance of the Cities class as a parameter. In this class write the following two methods that return JPanels (Hint: Many of the components on this gui will need to be accessed in other methods later. Initializing them in the constructor and storing them in instance variables now can save time later)
A public method named "getSearchPanel" that takes no parameters and returns a JPanel. This JPanel will contain a JTextField that will be a search bar, a JComboBox of type City that will contain the current search results, and a JButton that will set the map to the currently selected City. For now, add these three without any functionality. (In the example image this is the NORTH panel on the JFrame)
A public method named "getMapPanel" that takes no parameters and returns a JPanel. This method will contain 9 JLabels in a 3x3 grid where each JLabel will eventually contain a map tile image. For now, do not add anything to these JLabels. To get the JLabels to line be organized properly we will use a different layout manager for this panel. We will use the GridLayout with 3 rows and 3 columns which can be achieved with the code "panel.setLayout(new GridLayout(3, 3));" assuming your panel is stored in a variable named panel. With this layout components will be added to the panel in a 3x3 grid in row major order starting with the upper left (same order as reading a page). For example, the first 3 elements added to this panel will be placed in the first row, then the next 3 elements will be added to the second row. (In the example images this is the CENTER panel on the JFrame)
In the maps package, create a new class named "Main" and write a main method in this class. This class will be the only one with a main method or a JFrame. Do not add a JFrame anywhere else in your project. In this class you can test and play your game by setting up a JFrame in the same way we've been using in the class and adding the two GUI panels to the locations given above. You will need to create new Cities and MapGUI instances to setup this JFrame. The JFrame in the image was sized by calling frame.pack();
Objective 2: Cities
For this objective we will read the cities data and store it in a data structure. We will also write several methods that will be called to add city functionality to the program.
Tasks
In the cities package, create a new class named "City" that will store the information for a city. In this class implement the following:
Override the toString method to return the data of the city in the format "[population] - [name], [region], [country]". For example, Buffalo, NY would be printed as "279557 - buffalo, NY, us"
Implement anything else in this class that you will need to complete the project. You may want to include a constructor and getter methods, though you are free to design these methods however you would like in order to achieve the toString functionality (ex. population, name, region, and country will need to be stored in instance variables)
In the cities package, implement the existing "Cities" class to store all the cities data by writing:
A public constructor that takes a String as a parameter. This String will be the filename of the cities.csv file (including the directory path) and the constructor will parse this file and store the data in an instance variable(s) (Hint: An ArrayList of type City can store all this data)
A public method named "getDefaultCity" that takes no parameters and returns an instance of a City. The returned City will represent Buffalo, NY which will be used to initialize the map on our GUI. For this method, find Buffalo, NY in the stored cities and return it (Note: This method will be used to to test your City class. Calling toString on whatever is returned by this method should return "279557 - buffalo, NY, us")
A public method named "searchByName" that takes a String as a parameter and returns an ArrayList of type City. The input String should be interpreted as the search criteria provided by the user and the returned ArrayList will contain every City with a name that contains the input String, sorted by decreasing population. For example, if the input is "york" this method will return ["8107916 - new york, NY, us", "144202 - york, Q5, gb", "46010 - west new york, NJ, us", "39587 - york, PA, us", "15172 - yorkton, 11, ca", "3101 - hevizgyork, 16, hu", "2020 - vamosgyork, 11, hu"] in this order
Objective 3: Map Tiles
In this objective you will write the code that will connect to the tile server API and retrieve the correct tiles for a given city so we can display the map for that city.
Note: For all tiles in this project we will use a zoom level of 6 and the user will not be able to change the zoom level. Since you will need this value throughout the project, and you may want to change it during testing or after you complete all 5 objectives, you should store the zoom level in a variable in a single class. This can be done by creating a static variable so it can be accessed in every class without instantiating the class where it is defined (Example: This is how constants like Math.PI, Math.E, and Integer.MAX_VALUE are defined)
Tasks
In the maps package, create a new package named "tiles"
In the tiles package, create a new class named "Tile" which will represent a single map tile that can be requested from the API. You may implement whatever methods, constructors, and instance variables you need to represent a map tile through instances of this class as long the following methods are implemented:
A public method named "getTileURL" that takes no parameters and returns a String. This String will be url for the API that will retrieve the map tile represented by this instance of the TIle class at a zoom level of 6. Be sure to use the API at the server "http://a.tile.openstreetmap.org/" (note the "http", not "https", since this String must match exactly as expected in AutoLab you cannot be off by a single character)
A public method named "getImageIcon" that takes no parameters and returns an ImageIcon. This method will return a new ImageIcon for the url returned by the getTileURL method. Review the JLabel lecture notes for an example (you will add these ImageIcons to JLabels in a later objective)
In the tiles package, create a new class named "TileUtils" which will contain several static methods that will create the appropriate tiles for our maps. You will use this class as a library of static methods in the rest of your project. Since this class does not rely on any state that is stored in instance variables we will make the methods static and call them through the class instead of instantiating the class similar to how we use the methods in the Math class. In this class implement the following:
A public static method named "getTile" that takes a City as a parameter and returns a Tile. This method will return a new Tile representing the map tile containing the input City at zoom level 6 (Hint: This will involve projecting latitude/longitude onto x/y values in the Mercator projection)
A public static method named "getTileMap" that takes a Tile as a parameter and returns an ArrayList of Tiles. The returned ArrayList will contain the 9 map tiles for a 3x3 map with the input tile at the center. The tiles in the ArrayList should be in row-major order as described in objective 1. This implies that the input tile will be at index 4 in the returned ArrayList (In this project you will not have to address tile wrapping and will not be tested with inputs where this would be required, though you should think about what would happen if the input tile is adjacent to the Anti-Meridian or either of the poles. In these cases your x or y value would need to wrap around the tile set to get the geographically adjacent tiles. For example, if the reference tile has a x value of 0 you could not simply subtract 1 to get the tile directly to the west)
Objective 4: Search Cities
Now that you've written code that can utilize both the cities data and the map tiles API we are ready to combine these into a user feature on the GUI. By the end of this objective a user will be able to search for a city by name, be given a list of results, and select one to be shown on the map.
Tasks
In the MapGUI class write a public method named "setReferenceTile" that takes a Tile as a parameter and has return type void. Calling this method will update the GUI to display a map with the input reference tile as the center tile. By calling the tile methods you've written in the previous objective you can write this method with only a few lines of code. Recall that you will add the ImageIcons of each tile to it's corresponding JLabel by calling setIcon from each JLabel. Do not add new JLabels to the GUI, but add new Icons to the existing JLabels (Hint: It is recommended that you add a println each time you get an image icon. Since this method will require 9 API calls it will run rather slow. The print statements will provide a way for you to see whether or not your program is still making API calls)
When a new instance of MapGUI is created, initialize the map to show Buffalo, NY. This can be accomplished by calling getDefaultCity, getTile, and setReferenceTile without adding any additional code. After this task you should be able to run your Main method and see a map of Western New York
You may also need to add other methods, including getter methods for some components, to your MapGUI class to achieve the rest of the functionality in this project. You can add any other methods you would like to this and other classes
In the maps package, create a new package named "control". This package will contain all the Listeners that will control the interactions between the GUI and the cities/tile functionality. This is commonly referred to as glue code and follows the same MVC approach as the rhymes project
In the control package, create a new class named "SearchListener" that implements the KeyListener interface. This class will be attached to the text area and will update the combo box with search results each time the user types a character. In this class implement:
A public constructor that takes an instance of the MapGUI and Cities classes as parameters in this order and stores these in instance variables
Override the keyReleased method to set the values to the JComboBox to the results of searching for cities that contain the text in the JTextField sorted by decreasing population. Remember to remove all the elements from the combo box before adding the new search results. Note that you are not checking which key was released so this method will be called every time the user releases any key in the search box which gives us a very responsive search feature
Add an instance of this class to your JTextField
In the control package, create a new class named "CitySelectListener" that implements the ActionListener interface. This class will be attached to the button and will update the map to the currently selected City. In this class implement:
A public constructor that takes an instance of the MapGUI class as a parameter and stores this in an instance variable
Override the actionPerformed method to update the map (3x3 tile grid) on the GUI to a map centered around the currently selected City in the JComboBox
Add an instance of this class to your JButton
Objective 5: Movement with Mouse
The last feature we will add to this software is the ability for the user to move the map by clicking on the map near the direction in which they want it to move. We will accomplish this by adding a MouseListener to each of the map tiles that will listen for mouse presses and move in a direction appropriate for the tile that was clicked. For example, if the bottom tile was pressed (index 7 in the ArrayList) the map should move 1 position to the south. If the upper left tile was pressed the map should move 1 position to the north and 1 position to the west.
Tasks
In the control package, create a new class named "TranslateListener" that implements the MouseListener interface. This class will be attached to each JLabel containing a map tile and will control moving the map with the mouse. In this class implement:
A public constructor that takes an instance of the MapGUI class and 2 ints as parameters and stores these in instance variables. The 2 ints should be interpreted as the change in x and y respectively. This will allow you to use this same class for all 8 directions of movement by using different ints in the constructor calls
Override the mousePressed method to move the map in the appropriate direction based on the 2 ints provided in the constructor call. For example, the ints 1 and -1 the map should add 1 to the current x value and subtract 1 from the current y value and reset the reference tile to these have these new values
Add an instance of this class to each of your JLabels that contain the map tiles with the appropriate ints in each constructor call to achieve the desired behavior of the map
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started