G.L.I.F.R.P. (Good Life Index For Real People)
Computer Science 2XB3
McMaster University, Faculty of Engineering
Group 05: Pasindu Gunasekara, Nick Morrison, Roberto Temelkovski, Juan Carlos Santana, Teodor Voinea
Version Number: 3 (Caterpillar)
April 11, 2016
Table of Contents
Table of Contents | Page 1 |
Revision Page | Page 2 |
Contribution Page | Page 3 |
Executive Summary | Page 4 |
Module Interface Specification (MIS) | Page 4-11 |
| |
Module Implementation Details (MID) | Page 12-18 |
| |
Class Diagram | Page 19 |
Uses Relationship | Page 20 |
Traceability Matrix | Page 20 |
Design Review | Page 21-23 |
Revision
The Revised Project can be found in Revised-Project.pdf (latest version in 2XB3 group dropbox).
By virtue of submitting this document we electronically sign and date that
the work being submitted by all the individuals in the group is their ex-
clusive work as a group and we consent to make available the application
developed through [CS] or [SE]-2XB3 project, the reports, presentations,
and assignments (not including my name and student number) for future
teaching purposes.
Team Members & Roles:
Name | Student Number | Role |
Pasindu Gunasekara | 001412155 | Programmer |
Nick Morrison | 001426613 | Front End Lead, Log Admin |
Juan Carlos Santana | 001411625 | Project Manager |
Roberto Temelkovski | 001418731 | Programmer |
Teodor Voinea | 001409586 | Back End Lead |
Contribution Page
Name | Role | Contribution |
Pasindu Gunasekara | Programmer | Parse Data Sets, Query Information, programmer, Design Document |
Nick Morrison | Front End Lead, Log Admin | Front end, UI, testing, project log, Requirement Specs Document |
Juan Carlos Santana | Project Manager | Project Plan, Scoring Algorithm,Crime Weighting, Revised Requirement Specs Document, Revised Project Document |
Roberto Temelkovski | Programmer | Graph, Graph searching algorithms, programmer,testing, Design Document |
Teodor Voinea | Back End Lead | Set up backend Server and host on AWS, Sorting Algorithm, Design Document |
USES
None
VARIABLES
None
ACCESS PROGRAMS
getLat() : String
Returns the latitude of the city as String
getLong() : String
Returns the longitude of the city as String
getCrime() : double
Returns the crime rate of the city as double
getZip() : String
Returns the zip code as String
getPrice() : double
Returns the price as a double
getScore() : double
Returns the score as double
getName() : String
Returns the name as String
getState() : String
Returns the abbreviated state as String
getUState() : String
Returns the un-abbreviated state as String
getPopulation() : int
Returns the population as integer
getViolentCrime() : int
Returns the violent crime as integer
getMurder() : int
Returns the murder as integer
getRape() : int
Returns the rape as integer
getRobbery() : int
Returns the robbery as integer
getAssault() : int
Returns the assault as integer
getProperty() : int
Returns the property as integer
getBurglary() : int
Returns the burglary as integer
getMotor() : int
Returns the motor incidents as integer
getArson() : int
Returns the arson as integer
getPlace_name() : String
Returns the place name as String
getPlace_id() : String
Returns the place id as String
getIndex_nsa() : double
Returns the non adjusted as double
getIndex_sa() : double
Returns the seasonally adjusted as double
getYear() : int
Returns the year as integer
getPriceInflation() : double
Returns inflation as double
setInflation() : void
Calculates inflation based on index_sa and index_sa
setCrime(double d) : void
Takes a double and sets the crime value
setZip(String zip) : void
Takes a String and sets the zip code
setPrice(double d) : void
Takes a double and sets the price
setScore() : void
Calls a private method to calculate the score
setLat(String lat) : void
Takes a String and sets the latitude
setLong(String longe) : void
Sets the longitude for the City.
setState(String state) : void
Takes a String and sets the abbreviated state
setUState(String ustate) : void
Takes a String and sets the unabbreviated state
setName(String name) : void
Takes a String and sets the name
setPopulation(int population) : void
Takes an integer and sets the population
setViolentCrime(int violentCrime) : void
Takes an integer and sets the violent crime
setMurder(int murder) : void
Takes an integer and sets the murder
setRape(int rape) : void
Takes an integer and sets the rape
setRobbery(int robbery) : void
Takes an integer and sets the robbery.
setAssault(int assault) : void
Takes an integer and sets the assault.
setProperty(int property) : void
Takes an integer and sets the property.
setBurglary(int burglary) : void
Takes an integer and sets the burglary.
setLarceny(int larceny) : void
Takes an integer and sets the larceny.
setMotor(int motor) : void
Takes an integer and sets the motor incidents.
setArson(int arson) : void
Takes an integer and sets the arson.
setPlace_name(String place_name) : void
Takes a String and sets the place_name.
setPlace_id(String place_id) : void
Takes a String and sets the place_id.
setPeriod(int period) : void
Takes an integer and sets the period.
setIndexNSA(int index_nsa) : void
Takes an integer and sets the index_nsa.
setIndexSA(int index_sa) : void
Takes an integer and sets the index_sa.
setYear(int year) : void
Takes an integer and sets the year
compareTo(Comparable o, int attribute) : void
Takes a City object and an integer (which is used to represent the different city attributes) and returns -1 if the argument city object is larger, 0 if the objects are equals or 1 if the argument city object is smaller.
equals(City c) : boolean
Takes a City object and compares their names, true if they’re equal, false otherwise.
USES
OpenStreetMapWrapper
Query
USA
VARIABLES
None
ACCESS PROGRAMS
main() : void
The main entrance for the program. Starts the server and serves GET/POST requests.
USES
None
VARIABLES
None
ACCESS PROGRAMS
buildByZip(String zip_code) : void
Takes in a String zip_code. Queries openstreetmap with zip_code. Based on the response, sets city name, state, latitude, longitude
buildByLatLong(String[] latlong) : void
Takes in an array of length 2 of Strings [latitude,longitude]. Queries openstreetmap with lat/long. Based on the response, sets city name, state, zip code.
buildByCityState(String city_name, String city_state) : void
Takes in 2 Strings (city name, state name). Queries openstreetmap with city name and state name. Based on the response, sets city name, state, latitude, longitude, zip code.
getZip() : String
Returns zipcode of the previously queried object as String.
getLat() : String
Returns latitude of the previously queried object as String.
getLon() : String
Returns longitude of the previously queried object as String.
getName() : String
Returns name of the previously queried object as String.
getState() : String
Returns state of the previously queried object as String.
Sorting Specification
USES
None
VARIABLES
None
ACCESS PROGRAMS
SortByType(int attribute, Comparable[] CitiesArray) : void
Sorts the Comparable array using the best sorting algorithm based on the attribute passed in.
USES
Node
Edge
VARIABLES
None
ACCESS PROGRAMS
getNodeCount() : int
Returns the amount of nodes in the graph
getNode(int id) : Node
Takes in an id and returns the node with that id
addNode(Node n) : void
Takes in a Node n and adds it to the list of nodes
connectNodes(int weight, Node i, Node j): void
Creates an edge between Nodes i and j with the specified weight, stores it in the list of edges
contains(Node n): boolean
Takes in a Node and returns true if that Node is in the graph, false if not
isEmpty(): boolean
Returns true if there is no nodes in this graph, false if not
Node Specification
USES
None
VARIABLES
None
ACCESS PROGRAMS
getId() : int
Returns the ID of the node
setMarked(boolean marked) : void
Takes in a boolean to set the marked property of this node (whether it has been visited)
isMarked() : boolean
Returns true if the node has been visited, false if it has not
getAdjacent(): ArrayList<Node>
Returns the list of adjacent nodes as an arraylist
getNextAdjacent(): ArrayList<Node>
Return the next adjacent node from the list of adjacent nodes (whatever node is at adjacentCount’s position)
addAdjacent(Node n): void
Add a new node to the list of adjacent nodes
toString(): String
Returns the string representation of the node
equals(Node node): boolean
Takes in another Node and returns true if they have the same Id
hasNeighbour(Node node): boolean
Takes in another Node and returns true if it is in the adjacency arraylist of this node
USES
Node
VARIABLES
None
ACCESS PROGRAMS
getVerticies(): Node[]
Return the two nodes that are connected by this edge
hasVertex(Node v) : boolean
Takes in a Node object, and returns true if one of the nodes connected by this edge is the passed in node
getWeight() : int
Returns the weight of the edge
State Specification
USES
Query
City
Sort
Node
VARIABLES
None
ACCESS PROGRAMS
insertCity(String c) : void
Takes in a City object or a List of City Objects and adds it/them to the list of Cities
compareState(City c) : void
Takes in a City and returns true if the city’s state is the same as this state
contains(City c) : void
Takes in a City and checks to see whether this
findLowestCrimeRate(int length) : ArrayList<City>
Takes in a number length and returns n cities with the lowest crime rate in this state
equalsName(State node) : void
Takes in either a Node or the name of the State (as a String) and returns true if the current state is equal to the parameter, else returns false.
equalsCode(State node) : void
Takes in a Node State and returns true if the current state is equal to the parameter, else returns false.
equalsCode(State node) : void
Takes in a code of the State and returns true if the current state is equal to the parameter, else returns false.
equalsName(String node) : void
Takes in a code of the State (as a String) and returns true if the current state is equal to the parameter, else returns false.
setName(String name) : void
Takes in a name and sets the of the state to this name
getName():String
Returns the name of this state
setCode(String code) : void
Takes in the state code of the state and sets the state code of this state to equal that state code
getCode() : String
Returns the state code
toString() : String
Returns the string representation of the state
USES
State
City
Node
Graph
Query
VARIABLES
None
ACCESS PROGRAMS
findStateByState(State s) : State
Takes in a State object and returns the corresponding state
findStateByStateName(State s) : State
Takes in the State’s name and returns that matches that name
findStateByCity(City c) : State
Takes in a City and finds the State which contains this City
getNeighbouringStates(State state, int distance) : ArrayList<State>
Takes in a start state and a distance (states away) and returns all the the states within the distance of the start state
getHops(State state, State state2) : int
Get the amount of hops between two states
findLowestCrimeRate(ArrayList<State> states,int length) : ArrayList<City>
Takes in a list of states and an amount of cities n, returns the n lowest crime rate cities in the states passed in. Alternatively, this can take just an amount of cities n and returns the n lowest crime rate cities in the entire united states.
findLowestCrimeRate(int length) : ArrayList<City>
Takes in a list of states and an amount of cities n, returns the n lowest crime rate cities in the states passed in. Alternatively, this can take just an amount of cities n and returns the n lowest crime rate cities in the entire united states.
addCity(String c) : void
Add a City to the state
containsState(State state) : boolean
Takes in a State and returns true if that state is already in the list of states, false otherwise
printUSA() : void
Prints out information about all the states in the USA data structure
USES
City
VARIABLES
None
ACCESS PROGRAMS
getCityData(String city) : City
Populates City object with data for the city c. Returned as a new City object.
getLowestCrime(String state, int n) : ArrayList<City>
Returns a list of cities of size n, that have the lowest crime rates within the state, state, as a list of strings.
getCityByCrime(City c) :
Gets housing data for city c, and returns it as a City object.
getStateNames() : ArrayList<String>
Returns a list of all the states in the United States as Strings.
getCityNames(String state) : ArrayList<String>
Returns a list of all cities within the state, state, as Strings.
USES
OpenStreetMapWrapper
Query
USA
VARIABLES
usa : Usa
USA object used to get information about all the states, and cities within the United states in order to create the initial Graph.
gson : Gson
A Gson object used to respond to GET requests, with information about a city.
Mapwrap : OpenStreetMapWrapper
OpenStreetMap wrapper object used to fill in missing information about latitude and longitude within city objects.
q : Query
Used to query information about a city or state from the Query class.
ACCESS PROGRAMS
main() : void
The main entrance for the program. Starts the server and serves GET/POST requests. Creates the following endpoints, /searchLCMByState, /search, /index. Instantiates the USA graph in order for it to be used for searching. The spark library handles the asynchronous calls to the lambda functions built into main. The lambda functions are what allow us to build all of the endpoints. Additionally, the whole function is wrapped in a try-catch so as to not crash the server on a bad input. The server response on error creates a 500 HTTP error code and prints the stacktrace.
City Implementation
USES
None
VARIABLES
lat : String
Holds the latitude of the City.
lon : String
Holds the longitude of the City.
zip : String
Holds the zip code of the City.
crime : double
Holds the crime rate of the city.
price : double
Holds the price inflation of the City.
Score : double
Holds the score that the city will receive once its calculated.
State : String
Holde the abbreviated name of the state.
uState : String
Holds the full name of the state.
population,violentCrime, murder, rape,robbery, assault, property , burglary, larceny, motor, arson : int
All the data for crimes that get pulled from a query. These are
all the different crime that will be used for scoring and throughout the GLIFRP program.
place_name : String
Holde the name of the City
place_id : int
Holds a unique City Id
index_nsa, index_sa, inflation : double
Variables that get pulled from a query and are used to calculate the score.
period : int
Holds the time period of price indices.
Year : int
Holds the year of the price indices
ACCESS PROGRAMS
calculateScore(int crimeWeight, int indexWeight) : void
Calculates the score based on the variables set in the private class. The weight for the different crimes are wtArson = 8,
wtAssualt = 8, wtBurglary = 5, wtLarceny = 7, wtMotor = 5 , wtMurder = 15, wtProperty = 7, wtRape = 10, wtRobbery = 5,
wtViolentCrime = 15. The maxScore is 500. It calculated all the crimes with the weights then divides by the city population. Then it takes that resualt and multiplies it by the crimeWieght variables. The subtracts that result from the maxScore.
calculateCrimeRate() : double
Takes the sum of all the crimes divided by the population of the city.
totalCrime() : double
Calculates the total crime based on all the crime weights. (Sum of all the crimes).
USES
None
VARIABLES
sortBy : int
The attribute by which to sort the given Comparable array
ACCESS PROGRAMS
mergeSort(Comparable[] a, Comparable[] aux, int lo, int hi) : void
Sorts the array of Comparables using merge sort
quickSort(Comparable[] a, int lo, int hi) : void
Sorts the array of Comparables using quick sort
quickSort3way(Comparable[] a, int lo, int hi) : void
Sorts the array of Comparables using 3 way quick sort
shellSort(Comparable[] a) : void
Sorts the array of Comparables using shell sort
heapSort(Comparable[] pq) : void
Sorts the array of Comparables using heap sort
sink(Comparable[] pq, int k, int N) : void
Sinks the item at index k in the Comparable array pq of size N
exch(Object[] a, int i, int j) : void
Exchanges the object at index i in the Object array a with the object at index j
partition(Comparable[] a, int lo, int hi) : int
less(Comparable v, Comparable w) : boolean
Compares the Comparable v with Comparable w and returns true if v < w
less(Comparable[] pq, int i, int j) : boolean
Compares the item at index i in the Comparable array pq with the item at index j and returns true if pq[i] < pq[j]
merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) : void
Merges the sub array a[lo to mid] with a[mid to hi] in a sorted manner
USES
None
VARIABLES
adjacent : ArrayList<Node>
adjacentCount : int
id : int
visited : boolean
USES
Node
VARIABLES
v1 : Node
v2 : Node
weight: int
USES
Query
City
Sort
Node
VARIABLES
name : String
Name of the state
code : String
Code for the state (IE Arizona’s code would be “AZ”
cities : ArrayList<String>
A list of the names of the cities in the state
q : Query
The object used to make all the SQL queries
ACCESS PROGRAMS
insertCity(String c) : void
Takes in a City object or a List of City Objects and adds it/them to the list of Cities
compareState(City c) : void
Takes in a City and returns true if the city’s state is the same as this state
contains(City c) : void
Takes in a City and checks to see whether this
findLowestCrimeRate(int length) : ArrayList<City>
Takes in a number length and returns n cities with the lowest crime rate in this state
equalsName(State node) : void
Takes in either a Node or the name of the State (as a String) and returns true if the current state is equal to the parameter, else returns false.
equalsCode(State node) : void
Takes in a Node State and returns true if the current state is equal to the parameter, else returns false.
equalsCode(State node) : void
Takes in a code of the State and returns true if the current state is equal to the parameter, else returns false.
equalsName(String node) : void
Takes in a code of the State (as a String) and returns true if the current state is equal to the parameter, else returns false.
setName(String name) : void
Takes in a name and sets the of the state to this name
getName():String
Returns the name of this state
setCode(String code) : void
Takes in the state code of the state and sets the state code of this state to equal that state code
getCode() : String
Returns the state code
toString() : String
Returns the string representation of the state
USES
Node
Edge
VARIABLES
nodes : ArrayList<Node>
The list of all the nodes in the graph
edges : ArrayList<Edge>
The list of all edges in the graph
ACCESS PROGRAMS
edgeWith(Node v1, Node v2) : Edge
Takes in two nodes and returns the edge that connects the two nodes if there is one. Returns null if no edge connects these nodes
USES
Node
Edge
Query
Graph
VARIABLES
nodes : ArrayList<Node>
The list of all the nodes in the graph
edges : ArrayList<Edge>
The list of all edges in the graph
q : Query
Object used for all the SQL queries
currentId : int
An ID counter that keep tracks of how many states have been made so that not states have the same ids
ACCESS PROGRAMS
limitedBFSSearch(State state, int distance) : ArrayList<State>
Takes in a state object, and a distance (how many layers of BFS to work with).
Then it performs a BFS and returns the list of all the states within the layers searched
findDistance(State state, State end): int
Takes in a start state and an end state, runs a BFS from the start state to find how many layers have to be explored before you reach the end state. When it finds the end states, it returns the number of layers explored (which is the state distance from the start state to the end state)
lcmHelper(ArrayList<State> states, int length) : void
Helper function that takes in an array to hold all the cities, a number of cities named length and state. Runs a DFS and adds length cities from each state to the cities array.
addState(State c) : void
Takes in a State object and adds it to the list of states
generateState() : void
Reads in a file of state pairs and for each creates the state objects and connects the relevant state objects together. Afterwards you are left witha fully loaded graph that is connected where it should be
getStateFromStateCode(String s): State[]
Takes in a state codes and gives the corresponding state objects (“AZ” returns “Arizona” object)
DFS(Object target, Node n, int flag) : void
Takes in an object to work with, a start node and a flag for the operation. The ADD_CITY_FLAG allows you to find the state that a city should be in and inserts it (The first parameter object is your city).
rDFS(Object target, Node curNode, int flag): Object
Takes in an object to work with, a start node and a flag for the operation. FIND_STATE_BY_STATE_FLAG takes in a state as the first parameter, and runs DFS to find the State that corresponds to the state passed in and returns it. The FIND_STATE_BY_CITY_FLAG takes in a City as the first object,runs DFS and returns the State that the city is found in. The FIND_STATE_BY_STATE_NAME_FLAG takes in a state name, runs DFS and returns the state object which has the name that you passed.
Query Implementation
USES
None
VARIABLES
dbc, dbh : String
dbc holds the connection information for the crime database, dbh holds the connection information for the housing and the coordinate database. Sample connection string: “jdbc:sqlite:databaseName.db”
queryCityData, queryCrimeData, queryCityData, queryNames : String
Holds the SQL query string for each type of query, crime, housing, and coordinate. Sample query string: “SELECT * FROM db WHERE id=’25’;”
city : City
City object to be populated when certain queries are called.
stateNames, cityNames : ArrayList<String>
When getStateNames() and getCityNames()are called, each of these ArrayLists are populated with names of all the states in the united states, or names of all the cities in a certain state.
lowestCrime : ArrayList<City>
When getLowestCrime(String state, int n) is called, this ArrayList is populated with an ‘n’ amount of cities with the lowest crime rates within the specified state. The size of the ArrayList will be the integer n.
state : Map<String, String>
A hashmap used to hold a set of state Abbreviated states, and abbreviated states. Can be used to switch between the two different strings.
ACCESS PROGRAMS
getCityData(String city) : city
Finds all the data in all three databases for information about the city specified in the argument String. All single quotes are filtered out with escaped quotes in order for the SQL statement to be processed correctly. Sets queryCityData for all data from the Cities database. Sets queryHousingData for all data from the Houses database for the maximum year (2014 or 2015), in order to avoid duplicates. Sets queryCrimeData to get all crime data for the city. Returns the global city object that is now populated with data from all three databases.
getStateCode() : void
Populates the states Hashmap with data about the abbreviated and abbreviated states.
getLowestCrime(String state, int n) : ArrayList<City>
Runs a query on the Crime database by changing the queryCrimeData String. It then populates the lowestCrime ArrayList with a list of cities of size n, that have the lowest crime rates within the state, state, as a list of strings. lowestCrime ArrayList is returned.
getCityByCrime(City c) : city
Assigns the city variable within the module to be the City object within the argument. Then queries all Housing, and coordinate data within that city. Returns the city object that was just modified.
getStateNames() : ArrayList<String>
Searches the Cities database for all unique state names and adds them to the stateNames ArrayList as strings. Returns a list of all the states in the United States as an ArrayList of Strings.
getCityNames(String state) : city
Searches the Cities database for all city names within the state specified in the argument and adds them to the cityNames ArrayList as strings. Returns a list of all the states in the United States as an ArrayList of Strings.
Query() : Constructor
Sets up the initial database connection strings, and populates the state code hashmap.
Where dotted arrows mean dependencies and use.
Uses relationship
Module (Class) Uses
Main Graph, City
Graph Edge, Node, Usa
Edge None
Node None
Usa State
City Crime
State Query,Sorting
Query None
Sorting None
Crime None
Traceability Matrix
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
Main | X | X | |||||||
Graph | X | X | |||||||
Sorting | X | ||||||||
City | X | X | |||||||
State | X | ||||||||
Query | X | X |
Requirements
1.score
2.crime stats
3.searching for cities
4.Make a Graph of USA
5.Search Through USA Graph
6.Sort Cities based on Attribute
7.Web Application BackEnd
8. Searching endpoints
9. Have all cities in a state
What the software does and its purpose? | It helps users find a new city to move to based on criteria they provide in the search query. |
The intended market and users of the software? | The intended market is the general US public which is currently looking to move to an area they may not be familiar. |
Basic functions of the software? | Allows the user to input a state and city (or just state), the software then returns 9 cities that meet the default criteria. |
Advanced functions of the software? | Allows the user to weigh the distance and crime rate of a city according to their needs to generate a more tailored response. Also, users can specify the quantity of cities they would like to see (more or less than the default: 9) by appending “, 10” at the end of their query. |
Quality | Comments are concise and descriptive. |
Completeness | Every class is commented using the JavaDocs system. All functions are commented, major variables and components are commented. |
Accuracy | Comments accurately describe the code. |
Appropriateness | Appropriate documents, not extensive, on point and expressive. |
Clarity | Clear and explanatory comments. Describes the algorithms and data structures used. |
README | Includes a README with styling using the github markdown. Includes images and text to describe running and testing the server. |
How straightforward is it to meet the build requirements on a build platform? | Requires Java 1.8 and Bower. |
How straightforward is it to build the software on the build platform? | Instructions are included in the README. After installing Java 1.8 and Bower, it’s only a matter of cloning the project from GitHub and executing the precompiled jar. |
How easy is it to install the software on a target platform? | Users interact with the software as a website. Therefore any user with a modern web browser that supports javascript and an internet connection can use the software. |
How easy is it to learn the basic functional tasks? | The UI follows very closely to that of Google Maps and many other mapping websites. Therefore basic functionality should be intuitive to any previous users of those services. |
How easy is it to the the advanced functional tasks? | Advanced tasks such as weighing crime rate and distance differently can be learned by experimenting with the search bar sliders. Selecting the quantity of cities to display is a hidden feature that can only be discovered by accident or reading the Git/provided examples. |
Extensibility | The software is easily extensible in many different ways. For example, adding new information for cities would simply require finding the new information in a database, adding new variables to the city class to store these new attributes, and adding the ability to sort by these new attributes. Updating previous databases with new information is also easy, so long the new information can be made to match the previous format of the database, no additional changes are required. The API is exposed publicly and can be used in other software projects. |
Modularity | The classes (Usa, State) inherit from more generic classes (Graph, Node) so any US specific changes within Usa and State would not impact any other country we could add in the future. Cities are the same across the world so the sorting and searching algorithms are completely independent of the country those cities belong to. The server only hosts the API and serves 1 index page from a public (not-compiled) folder, so any changes to the UI would not impact the server and vise-versa |
Concision | Clear variable names. Short functions and methods that perform only one unique task. |
Testing | We Incorporated physical distance as well as state dependent “hop” distance. Testing search/sort/graph algorithms with JUnit Testing for correctness. |
Validation. | The Scoring algorithm takes into account the type of crime and the population of the city. Since the algorithm takes into account population it gives a more accurate representation of the city, and allows the bigger cities to be fairly represented. Also, every crime was researched and each weighted according to punishments. A detailed analysis can be seen in the Crime_Correctness.pdf (can be found in the 2XB3 group dropbox). |