-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprice_estimation.php
153 lines (133 loc) · 4.99 KB
/
price_estimation.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
<?php
/**
* Convert data from a JSON file into a PHP object
* @param filename the name of the file
* @return the JSON info as a PHP object
*/
function getData($filename) {
$data = file_get_contents($filename);
//Fix JSON syntax error with decoding: https://stackoverflow.com/questions/17219916/json-decode-returns-json-error-syntax-but-online-formatter-says-the-json-is-ok
for ($i = 0; $i <= 31; ++$i) {
$data = str_replace(chr($i), "", $data);
}
$data = str_replace(chr(127), "", $data);
// This is the most common part
// Some file begins with 'efbbbf' to mark the beginning of the file. (binary level)
// here we detect it and we remove it, basically it's the first 3 characters
if (0 === strpos(bin2hex($data), 'efbbbf')) {
$data = substr($data, 3);
}
$data = json_decode($data);
return $data;
}
/**
* Calculates the great-circle distance between two points, with
* the Haversine formula. Credits: https://stackoverflow.com/questions/10053358/measuring-the-distance-between-two-coordinates-in-php
* @param float $latitudeFrom Latitude of start point in [deg decimal]
* @param float $longitudeFrom Longitude of start point in [deg decimal]
* @param float $latitudeTo Latitude of target point in [deg decimal]
* @param float $longitudeTo Longitude of target point in [deg decimal]
* @param float $earthRadius Mean earth radius in [m]
* @return float Distance between points in [m] (same as earthRadius)
*/
function haversineGreatCircleDistance($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius =6371000)
{
// convert from degrees to radians
$latFrom = deg2rad($latitudeFrom);
$lonFrom = deg2rad($longitudeFrom);
$latTo = deg2rad($latitudeTo);
$lonTo = deg2rad($longitudeTo);
$latDelta = $latTo - $latFrom;
$lonDelta = $lonTo - $lonFrom;
$angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
return $angle * $earthRadius;
}
/**
* Get the nearest locations to a given latitude and longitude
* @param $lat1 the latitude
* @param $lon1 the longitude
* @param $listings an array of listings
* @param $size the number of nearest locations to get
* @return an array of the nearest locations
*/
function nearestLocations($lat1, $lon1, $listings, $size) {
//array of closest objects
$closest = [];
for ($i = 0; $i < count($listings); $i++) {
$lat = $listings[$i]->latitude;
$lon = $listings[$i]->longitude;
$dist = haversineGreatCircleDistance($lat1, $lon1, $lat, $lon);
if (count($closest) < $size and $dist != 0) {
$closest[$dist] = $listings[$i];
} else if ($dist != 0) {
$closest = compareDist($closest, $dist, $listings[$i]);
}
}
return $closest;
}
/**
* Destructively modify the closest array to remove/replace the current farthest location in the array with a nearer one
* @param $closest the current location you are comparing
* @param $dist the distance between closest and listing
* @param $listing the location you are comparing against closest
* @return the modified array of closest locations
*/
function compareDist($closest, $dist, $listing) {
$maxDist = max(array_keys($closest)); //get farthest city from closest
if ($dist < $maxDist) {
unset($closest[$maxDist]); //remove and replace with the closer city
$closest[$dist] = $listing;
}
return $closest;
}
/**
* Calculate the estimated weekly income for a list of listings based off price and number of bookings (= number of reviews)
* @param $listings the array of listings
* @return the average weekly income of the listings
*/
function estimateWeekly($listings) {
$sum = 0.0;
$total = count($listings);
foreach ($listings as $key=> $val) {
//NOTE: equate reviews_per_month to bookings per month, divide by 4 to get bookings per week
//sum listings that have been "booked"
if ($val->reviews_per_month) {
$weekly = dollarToNumber($val->price) * ($val->reviews_per_month / 4.0);
$sum += $weekly;
}
}
$formatted = round($sum / $total, 2);
return number_format($formatted, 2); //keep trailing 0s
}
/**
* Strip dollar signs and convert dollar string to a number
* @param $dollar the dollar to convert
* @return the number value of the dollar string
*/
function dollarToNumber($dollar) {
return floatval(ltrim($dollar, '$'));
}
/**
* Calculate the average weekly income of a given location based off of user input from index.php
*/
function main() {
try {
//get user input
$lat = htmlspecialchars($_GET["lat"]);
$lon = htmlspecialchars(($_GET["lon"]));
//make sure latitude and longitude are valid
if (is_numeric($lat) && is_numeric($lon)) {
$listings = getData("data//listings_all.json");
$closest = nearestLocations($lat, $lon, $listings, 50);
$price = estimateWeekly($closest);
echo "$".$price;
} else {
echo "Please make sure your input is a valid geo-location.";
}
} catch (Exception $e) {
echo "Please make sure your input is a valid geo-location.";
}
}
main();
?>