Browse Source

First release of how I changed the challenge.

master
Rick Hays 6 years ago
parent
commit
82c0e30af4
  1. 183
      RX2.php
  2. 112
      assets/classes/geocode.php
  3. 31
      assets/data/pharmacies.csv
  4. BIN
      assets/db/RX.db
  5. 85
      assets/js/system.js
  6. 105
      index.html

183
RX2.php

@ -0,0 +1,183 @@
<?php
/**
* RX2 - Main API to process in coming request for closest location.
* 2019-09-29
* Rick Hays
*
*/
include_once 'assets/classes/geocode.php';
// See if we are in test mode //////////////////////////////////////////////////////////////////////////////////////
$TEST = GetParam('T', 0);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Test Key before allowing access - No encryption, simple key test.
$key = strtoupper(GetParam('K'));
if ($key !== 'RLH4321')
{
$result['status'] = 'ERROR';
$result['code'] = '100';
$result['description'] = 'Bad Key Code.';
$jsonOut = json_encode($result);
}
else
{
$request_method = strtoupper($_SERVER["REQUEST_METHOD"]);
$command = strtoupper(GetParam('C'));
$address = rawurldecode(GetParam('address')) . '';
switch ($request_method)
{
case 'GET':
switch ($command)
{
case 'LL':
if ($address === '')
{
$aOutput = array
(
'status' => 'ERROR',
'code' => '101',
'description' => 'No Address present.',
);
$jsonOut = json_encode($aOutput);
break;
} // if ($address === '')
$geo = new geocode();
$latlon = $geo->getLatLong($address);
$jsonOut = json_encode($latlon);
$geo = NULL;
break;
case 'DIST':
if ($address === '')
{
$aOutput = array
(
'status' => 'ERROR',
'code' => '101',
'description' => 'No Address present.',
);
$jsonOut = json_encode($aOutput);
break;
} // if ($address === '')
$geo = new geocode();
$db = new PDO('sqlite:assets/db/RX.db');
$latlon = $geo->getLatLong($address);
if ($latlon['status'] === 'OK')
{
$shortest = 999999.99; // <-- Sets to a very high value to find shortest distance below.
$pharmacies = $db->query('SELECT rowid,* FROM pharmacies');
$SKIP = 0; // Part of test mode to skip all but one record in DB
foreach ($pharmacies as $pharmacy)
{
$destination = $pharmacy['Address'] . ' ' . $pharmacy['City'] . ' ' . $pharmacy['State'] . ' ' . $pharmacy['Zip'];
if ($SKIP === 0) $result = $geo->getDistance($address, $destination);
if ($TEST == 1) $SKIP = 1;
if ($result['status'] === 'OK')
{
$distance = floatval(preg_replace('/[^0-9.]/', '', $result['distance']));
if ($distance < $shortest)
{
$aOutput = array
(
'pharmacy' => $pharmacy['Pharmacy'],
'address' => $pharmacy['Address'],
'city' => $pharmacy['City'],
'state' => $pharmacy['State'],
'zip' => $pharmacy['Zip'],
'latitude' => $pharmacy['Latitude'],
'longitude' => $pharmacy['Longitude'],
'home_lat' => $latlon['lat'],
'home_lon' => $latlon['lon'],
'status' => $result['status'],
'distance' => $result['distance'],
'duration' => $result['duration'],
);
$shortest = $distance;
} // if ($result['distance'] < $shortest)
} // if ($result['status'] === 'OK')
else
{
$aOutput = array
(
'status' => 'ERROR',
'code' => '104',
'description' => "Problem returning distance between address and pharmacy.\nAddress: " . $address . "\nPharmacy( ROWID: " . $pharmacy['rowid'] . " Name: " . $pharmacy['Pharmacy'] . ') ',
);
$jsonOut = json_encode($aOutput);
break;
} //else ($result['status'] === 'OK')
} // foreach ($pharmacies as $pharmacy)
} // if ($latlon['status'] === 'OK')
else
{
$aOutput = array
(
'status' => 'ERROR',
'code' => '103',
'description' => "Problem returning Lat / Lon.\nAddress: " . $address,
);
$jsonOut = json_encode($aOutput);
break;
} // else ($latlon['status'] === 'OK')
$jsonOut = json_encode($aOutput);
$db = NULL;
$geo = NULL;
break;
default:
$aOutput = array
(
'status' => 'ERROR',
'code' => '102',
'description' => "Invalid Command sent - " . $command,
);
$jsonOut = json_encode($aOutput);
break;
} // switch ($command)
break;
// case 'POST':
// break;
//
// case 'PUT':
// break;
//
// case 'DELETE':
// break;
//
default:
// Invalid Request Method
header("HTTP/1.0 405 Method Not Allowed");
exit;
} // switch ($request_method)
} // else ($key !== 'RLH4321')
echo $jsonOut;
// ##### END OF PROGRAM ################################################################################################
/**
* GetParam - Returns the GET or POST values, and allows for defaults if not present.
* @param $param_name
* @param null $default
*
* @return string|null
*/
function GetParam($param_name, $default=NULL)
{
global $_POST;
global $_GET;
$param_value = "";
if(isset($_POST[$param_name]))
$param_value = $_POST[$param_name];
else if(isset($_GET[$param_name]))
$param_value = $_GET[$param_name];
else if($param_value === '')
$param_value = $default;
return $param_value;
}

112
assets/classes/geocode.php

@ -0,0 +1,112 @@
<?php
/****************************************************
* Geocode Class - Collection of geocoding Utilities
*
* Created By: Rick Hays
* Date: 2011-09-12
*
* Revisions:
* 2011-09-12 RLH Added "straight_line_distance"
* 2011-09-12 RLH Added "getLatLong"
* 2019-09-28 RLH Added "getDistance"
*
*****************************************************/
require_once 'key.php';
class geocode
{
/*******************************************************************************************************************
* straight_line_distance - Returns Straight Line distance between two sets of (LAT/LONGS)
*
* @param float $lat1 - Latitude of Location #1
* @param float $lon1 - Longitude of Location #1
* @param float $lat2 - Latitude of Location #2
* @param float $lon2 - Longitude of Location #2
* @param string $unit - Unit of measure [DEFAULT = statute miles] ('K'=kilometers, 'N'=nautical miles)
*
* @return float - returns the distance in the desired unit of measure.
*/
public function straight_line_distance($lat1, $lon1, $lat2, $lon2, $unit='')
{
$theta = $lon1 - $lon2;
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
$dist = acos($dist);
$dist = rad2deg($dist);
$miles = $dist * 60 * 1.1515;
$unit = strtoupper($unit);
if ($unit == "K") { return ($miles * 1.609344); } // Return in Kilometers
else if ($unit == "N") { return ($miles * 0.8684); } // Return in Nautical Miles
else { return $miles; } // Return in Miles
}
/*******************************************************************************************************************
* getLatLong - Converts a string with a street address into Lat/Long coordinates using a Google call.
*
* @param string $address - Text address to convert.
*
* @return array
*/
public function getLatLong($address)
{
if (!is_string($address))die("All Addresses must be passed as a string");
$_url = sprintf('https://maps.googleapis.com/maps/api/geocode/json?address=%s&'.API_KEY,rawurlencode($address));
if($_result = file_get_contents($_url))
{
$decode = json_decode($_result,true);
$_coords['status'] = $decode['status'];
$_coords['lat'] = $decode['results'][0]['geometry']['location']['lat'];
$_coords['lon'] = $decode['results'][0]['geometry']['location']['lng'];
return $_coords;
}else die('Nothing was returned');
}
/*******************************************************************************************************************
* @param $origin
* @param $destination
* @param string $mode (driving, walking, bicycling)
* @param string $units (metric, imperial)
*
* @return array
* @example - https://maps.googleapis.com/maps/api/distancematrix/json?'.$this->api_key.'&origins='.rawurlencode($origin).'&destinations='.rawurlencode($destination).'&mode=driving&units=imperial&sensor=false
*
*/
public function getDistance($origin, $destination, $mode='driving', $unit='imperial')
{
// Set defaults to test for
$modes = array('driving', 'walking', 'bicycling');
$units = array('metric', 'imperial');
// Test that addresses are present
if (!is_string($origin) || !is_string($destination))die("All Addresses must be passed as a string");
// Test that Mode is present and of correct value
$mode = strtolower($mode);
if (!in_array($mode, $modes))die("Mode not correct");
// Test that Unit is present and of correct value
$unit = strtolower($unit);
if (!in_array($unit, $units))die("Unit not correct");
// Prep string for send ie(remove spaces and other objects)
$_origin = rawurlencode($origin);
$_destination = rawurlencode($destination);
$_mode = rawurlencode($mode);
$_unit = rawurlencode($unit);
$_url = sprintf('https://maps.googleapis.com/maps/api/distancematrix/json?%s&origins=%s&destinations=%s&mode=%s&units=%s&sensor=false',API_KEY, $_origin, $_destination, $_mode, $_unit);
if($_result = file_get_contents($_url))
{
$decode = json_decode($_result,true);
$_dist['status'] = $decode['status'];
$_dist['distance'] = $decode['rows'][0]['elements'][0]['distance']['text'];
$_dist['duration'] = $decode['rows'][0]['elements'][0]['duration']['text'];
return $_dist;
}else die('Nothing was returned');
}
} // End Class GEOCODE

31
assets/data/pharmacies.csv

@ -0,0 +1,31 @@
"name","address","city","state","zip","latitude","longitude"
"WALGREENS","3696 SW TOPEKA BLVD","TOPEKA","KS",66611,39.00142300,-95.68695000
"KMART PHARMACY ","1740 SW WANAMAKER ROAD","TOPEKA","KS",66604,39.03504000,-95.75870000
"CONTINENTAL PHARMACY LLC","821 SW 6TH AVE","TOPEKA","KS",66603,39.05433000,-95.68453000
"STORMONT-VAIL RETAIL PHARMACY","2252 SW 10TH AVE.","TOPEKA","KS",66604,39.05167000,-95.70534000
"DILLON PHARMACY","2010 SE 29TH ST","TOPEKA","KS",66605,39.01638400,-95.65065000
"WAL-MART PHARMACY ","1501 S.W. WANAMAKER ROAD","TOPEKA","KS",66604,39.03955000,-95.76459000
"KING PHARMACY","4033 SW 10TH AVE","TOPEKA","KS",66604,39.05121000,-95.72700000
"HY-VEE PHARMACY ","12122 STATE LINE RD","LEAWOOD","KS",66209,38.90775300,-94.60801000
"JAYHAWK PHARMACY AND PATIENT SUPPLY","2860 SW MISSION WOODS DR","TOPEKA","KS",66614,39.01505300,-95.77866000
"PRICE CHOPPER PHARMACY","3700 W 95TH ST","LEAWOOD","KS",66206,38.95792000,-94.62881500
"AUBURN PHARMACY","13351 MISSION RD","LEAWOOD","KS",66209,38.88534500,-94.62800000
"CVS PHARMACY","5001 WEST 135 ST","LEAWOOD","KS",66224,38.88323000,-94.64518000
"SAMS PHARMACY ","1401 SW WANAMAKER ROAD","TOPEKA","KS",66604,39.04160300,-95.76462600
"CVS PHARMACY","2835 SW WANAMAKER RD","TOPEKA","KS",66614,39.01550300,-95.76434000
"HY-VEE PHARMACY ","2951 SW WANAMAKER RD","TOPEKA","KS",66614,39.01215700,-95.76394000
"TALLGRASS PHARMACY","601 SW CORPORATE VIEW","TOPEKA","KS",66615,39.05716000,-95.76692000
"HUNTERS RIDGE PHARMACY","3405 NW HUNTERS RIDGE TER STE 200","TOPEKA","KS",66618,39.12984500,-95.71265400
"ASSURED PHARMACY ","11100 ASH ST STE 200","LEAWOOD","KS",66211,38.92663200,-94.64744000
"WALGREENS","4701 TOWN CENTER DR","LEAWOOD","KS",66211,38.91619000,-94.64036600
"PRICE CHOPPER PHARMACY","1100 SOUTH 7 HWY","BLUE SPRINGS","MO",64015,39.02931000,-94.27175000
"CVS PHARMACY","1901 WEST KANSAS STREET","LIBERTY","MO",64068,39.24385000,-94.44961000
"MARRS PHARMACY","205 RD MIZE ROAD","BLUE SPRINGS","MO",64014,39.02353000,-94.26060500
"WAL-MART PHARMACY ","600 NE CORONADO DRIVE","BLUE SPRINGS","MO",64014,39.02419300,-94.25503000
"WAL-MART PHARMACY ","10300 E HWY 350","RAYTOWN","MO",64133,38.98376500,-94.45930500
"HY-VEE PHARMACY ","9400 E. 350 HIGHWAY","RAYTOWN","MO",64133,38.99300000,-94.47083000
"HY-VEE PHARMACY ","625 W 40 HWY","BLUE SPRINGS","MO",64014,39.01070400,-94.27108000
"HY-VEE PHARMACY ","109 NORTH BLUE JAY DRIVE","LIBERTY","MO",64068,39.24575800,-94.44702000
"WALGREENS ","1701 NW HIGHWAY 7","BLUE SPRINGS","MO",64015,39.03754800,-94.27153000
"WALGREENS ","9300 E GREGORY BLVD","RAYTOWN","MO",64133,38.99534200,-94.47340000
"WALGREENS ","1191 W KANSAS AVE","LIBERTY","MO",64068,39.24387000,-94.44186400
Can't render this file because it contains an unexpected character in line 25 and column 0.

BIN
assets/db/RX.db

Binary file not shown.

85
assets/js/system.js

@ -0,0 +1,85 @@
function btnFind()
{
// Reset Rows
$("#alertBox").hide();
$(".alert").html("");
$('.alert').removeClass('alert-warning').removeClass('alert-danger').addClass('alert-warning');
$("#spinnerBox").hide();
$("#txtOutput").hide();
// Set Vars
let txtAddress = $("#txtAddress").val() + "";
// Quick test to make sure address was entered.
if (txtAddress === "")
{
$(".alert").html("You have to enter an address first.");
$("#alertBox").show();
return;
}
// Show Spinner
$("#spinnerBox").show();
// API - Call geocode API
// K = (Required) Key, allows access to API
// C = (Required) Command,
// LL - Will return the Lat/Lon of a given address.
// DIST - Will return the distance information.
// T = (Optional) Test, Test Mode = 1 will only pull one record from the DB
// ADDRESS = Address you want to return the Lat/Lon or Distance from.
$.ajax(
{
url : 'https://rhays.us/Examples/RX_API_Two/RX2.php',
type : 'GET',
dataType: 'json',
data :
{
'K' : 'RLH4321',
'C' : 'DIST',
'T' : '1',
'address' : txtAddress,
},
success : function(data)
{
switch(data.status.toUpperCase())
{
case 'OK':
sOutput = '<p>The Pharmacy closest to the address location you entered is: </p>';
sOutput = sOutput + 'Pharmacy: <b>' + data.pharmacy + '</b><br/>';
sOutput = sOutput + 'Address: <b>' + data.address + '</b><br/>';
sOutput = sOutput + 'City: <b>' + data.city + '</b><br/>';
sOutput = sOutput + 'State: <b>' + data.state + '</b><br/>';
sOutput = sOutput + 'Zip: <b>' + data.zip + '</b><br/><br/>';
sOutput = sOutput + '<p>Approx. Drive time and distance:</p>';
sOutput = sOutput + 'Distance: <b>' + data.distance + '</b><br/>';
sOutput = sOutput + 'Duration: <b>' + data.duration + '</b><br/>';
$("#txtOutput").html(sOutput);
$("#spinnerBox").hide();
$("#txtOutput").show();
break;
case 'ERROR':
$("#spinnerBox").hide();
$('.alert').removeClass('alert-warning').removeClass('alert-danger').addClass('alert-danger');
$(".alert").html(JSON.stringify('Error code: ' + data.code + ' - ' + data.description));
$("#alertBox").show();
break;
}
},
error : function(request,error)
{
$("#spinnerBox").hide();
$('.alert').removeClass('alert-warning').removeClass('alert-danger').addClass('alert-danger');
$(".alert").html('SYSTEM.JS<br/> ' + JSON.stringify(request));
$("#alertBox").show();
return;
},
// TODO: Hook to put in status codes
// statusCode:
// {
// 404: function() {
// alert( "page not found" );
// }
// },
});
}

105
index.html

@ -1,55 +1,58 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>RX_API_Two</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/styles.css">
<script src="assets/js/system.js"></script>
</head>
<body>
<head> <div class="container">
<meta charset="utf-8"> <div><p>&nbsp;</p></div>
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no"> <div class="row">
<title>RX_API_Two</title> <div class="col-md-12" style="background-color: #bca59e;">
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css"> <h1 class="text-center">RX API Two</h1>
<link rel="stylesheet" href="assets/css/styles.css"> </div>
</head> </div>
<div><p>&nbsp;</p></div>
<body> <div class="row">
<!-- Start: 2 Rows 1+3 Columns --> <div class="col-md-12 text-center align-self-center"><strong>Enter your address to find the closest pharmacy near you.</strong></div>
<div></div> </div>
<!-- End: 2 Rows 1+3 Columns --> </div>
<!-- Start: 2 Rows 1+1 Columns --> <div class="container">
<div> <div><p>&nbsp;</p></div>
<div class="container"> <div id="alertBox" class="row collapse hide">
<div class="row"> <div class="col-md-12 text-center align-self-center">
<div class="col-md-12" style="background-color: #bca59e;"> <div class="alert alert-warning" role="alert">
<h1 class="text-center">RX API Two</h1> <span><strong>Alert</strong> text.</span>
</div> </div>
</div> </div>
<div class="row"> <div><p>&nbsp;</p></div>
<div class="col-md-12 text-center align-self-center"><strong>Please enter your address to find the closest pharmacy near you.</strong></div> </div>
</div> <div class="row">
</div> <div class="col-md-12 text-center align-self-center m-auto"><input id="txtAddress" class="form-control-lg" type="text" maxlength="255" required="" placeholder="Type Address Here" autofocus="" style="width: 300px;"></div>
</div> </div>
<!-- End: 2 Rows 1+1 Columns --> <div><p>&nbsp;</p></div>
<!-- Start: 2 Rows 1+1 Columns --> <div class="row">
<div> <div class="col-md-12 text-center align-self-center"><button id="btnFind" class="btn btn-primary" type="button" onClick="btnFind()">Find Closest</button></div>
<div class="container"> </div>
<div class="row"> </div>
<div class="col-md-12 text-center align-self-center m-auto"><input class="form-control-lg" type="text" maxlength="255" required="" placeholder="Type Address Here" autofocus="" style="width: 300px;"></div> <div class="container">
</div> <div><p>&nbsp;</p></div>
<div class="row"> <div id="spinnerBox" class="row collapse hide">
<div class="col-md-12 text-center align-self-center"><button class="btn btn-primary" type="button">Find Closest</button></div> <div class="col-md-12 text-center align-self-center">
</div> <span class="spinner-border spinner-border-lg" role="status"></span>
</div> </div>
</div> </div>
<!-- End: 2 Rows 1+1 Columns --> <div id="txtOutput" class="row collapse hide" style="background-color: beige;">
<!-- Start: 1 Row 1 Column --> <div class="col-md-12"></div>
<div> </div>
<div class="container"> </div>
<div class="row">
<div class="col-md-12"></div>
</div>
</div>
</div>
<!-- End: 1 Row 1 Column -->
<script src="assets/js/jquery.min.js"></script>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
</body>
<script src="assets/js/jquery.min.js"></script>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
</body>
</html> </html>
Loading…
Cancel
Save