Appointments +  auto-generate map from additional field input

I'm thinking of using the Appointments + plugin on a project I'm working on.

My question:
If the nature of the business does not have a central location and is purely "at-client home" service, is there a way to use the additional fields so the client can input an address and have a map auto-generate?

  • Michael

    Hi Michael

    What exactly are you trying to achieve? Just display a map of their location? Display a map with their location and the business location? Something else?

    My business works in this way, where we go to our customers. I've written code for our website which takes the user inputted postal code, calculates the travelling time from us to them and grabs their full address using the Google Maps API .

    It then displays their address, a map showing our location and their location, and a cost to visit them which varies depending upon the travel time to their address. It also sends a push notification with all the details of every quote to me so that I know what customers have been quoted.

    It's not integrated with Appointments+, but I'm more than happy to share the code if that's the sort of thing you are looking to achieve.

    Regards,

    Michael

  • Michael

    OK so in the interests of full disclosure let me first off point out that this is not my day job! I'm a hobbyist coder and this was never intended for release so my code is probably horribly inefficient and riddled with errors, but it works (for me at least). I've added some annotations so you should be able to figure out what you need to change to customise it for your needs.

    The first bit is some javascript for geolocation, so website visitors can just click a button and the map and travelling time will be displayed based upon their current location (if it's available).

    <p id="errors"></p> <input type="image" alt="Use current location" id="gpsicon" src="https://www.yourwebsite.com/wp-content/uploads/crosshair.png" onclick="getLocation()">
    <p>
    <script>
    function getWidth() {
    document.getElementById('width').value = window.innerWidth;
    }
    var x = document.getElementById("errors");
    var image = document.getElementById('gpsicon');
    x.innerHTML = "<font size='+2'>Click here to check our coverage and prices using your current device location.</font>";
    function getLocation() {
        if (navigator.geolocation) {
            image.src = "https://www.yourwebsite.com/wp-content/uploads/loading-animation.gif";
     x.innerHTML = "<font size='+2'>Getting your location, please wait...</font>";
    navigator.geolocation.getCurrentPosition(showPosition, showError, {enableHighAccuracy: true, timeout: 15000, maximumAge: 600000});
        } else {
            image.src = "https://www.yourwebsite.com/wp-content/uploads/crosshair.png";
    x.innerHTML = "<font size='+2'>Geolocation is not supported by this browser.</font>";
        }
    }
    function showPosition(position) {
    document.getElementById('latitude').value = position.coords.latitude;
      document.getElementById('longitude').value = position.coords.longitude;
    document.getElementById('width').value = window.innerWidth;
    document.getElementById('quote').submit();
    }
    function showError(error) {
        switch(error.code) {
            case error.PERMISSION_DENIED:
               image.src = "https://www.yourwebsite.com/wp-content/uploads/crosshair.png";
    x.innerHTML = "<font size='+2' color='red'>Your settings do not allow us access to your location details, please enter your postcode instead.</font>"
                break;
            case error.POSITION_UNAVAILABLE:
               image.src = "https://www.yourwebsite.com/wp-content/uploads/crosshair.png";
    x.innerHTML = "<font size='+2' color='red'>Location information is currently unavailable, please enter your postcode instead.</font>"
                break;
            case error.TIMEOUT:
               image.src = "https://www.yourwebsite.com/wp-content/uploads/crosshair.png";
    x.innerHTML = "<font size='+2' color='red'>The request to get your location timed out, please enter your postcode instead.</font>"
                break;
            case error.UNKNOWN_ERROR:
               image.src = "https://www.yourwebsite.com/wp-content/uploads/crosshair.png";
    x.innerHTML = "<font size='+2' color='red'>An unknown error occurred, please enter your postcode instead.</font>"
                break;
        }
    }
    </script>

    Then there's the HTML for the form for the user to enter their postcode. The hidden fields are automatically populated with latitude and longitude if they hit the geolocation button. The width attribute is the value in pixels of their browser window width, this is automatically filled in by the javascript. I used the width so that below a certain value the returned Google map changes from high res to low res. The low res version shows less map features so looks less cluttered on a smaller screen.

    <form method="post" id="quote" action="https://www.yourwebsite.com/your-prices-page" />Or enter a postcode to check coverage and pricing for any area:<p>
    <input autocomplete="on" id="postcode" maxlength="8" name="postcode" size="15" type="text" placeholder="Postcode" /><a id="price"></a><input type="hidden" name="latitude" id="latitude" /> <input type="hidden" name="longitude" id="longitude" /> <input type="hidden" id="width" name="width" /><input type="submit" id="press" onclick="getWidth()" /></form>

    Finally there's the PHP. I'll add this in another reply as I'm pretty sure this forum won't accept PHP in posts so if I include it here then this whole post will probably fail...

  • Michael

    OK, here goes with the PHP

    $postcode2 = preg_replace('/[-\s.]+/', '', $_POST['postcode']);
    $postcode1 = $postcode2;
    if ((empty($_POST['postcode'])) && (isset($_POST['postcode']))) {
    $postcode1 = "{$_POST['latitude']},{$_POST['longitude']}";
    }
    
    /* If you aren't in the UK delete this next part or change it to suit your location. I added it as if users input a partial postcode eg W3 then Google would match it to a location in a different country and return a map showing half the world */
    if ((!empty($_POST['postcode'])) && (isset($_POST['postcode']))) {
    $postcode1 .='+UK';
    }
    
    /* Change the values in capitals to your own. See Google distance matrix API for valid location formats. The value I have entered for departure_time is 9am on a random Wednesday morning next year. Travel time will be returned using historic data for the journey assuming you were setting off at this time, so you may wish to change it to something better suited to your appointment booking times. */
    $url = "https://maps.googleapis.com/maps/api/distancematrix/json?origins=ENTER-YOUR-LOCATION&destinations=$postcode1&mode=driving&language=en-EN&avoid=tolls&departure_time=1485936000&traffic_model=best_guess&key=ENTER-GOOGLE-DISTANCE-MATRIX-API-KEY";
    
    $data = @file_get_contents($url);
    
    $result = json_decode($data, true);
    
    foreach($result['destination_addresses'] as $addy)
    foreach($result['rows'] as $distance) {
    
    $status = $distance['elements'][0]['status'];
    $traveltime = $distance['elements'][0]['duration_in_traffic']['value'];
    }
    /* $traveltime will be calculated travel time in seconds, $tomin is travel time in minutes */
    $tomin = $traveltime;
    $tomin /= 60;
    
    /* Errors to display if user input generates bad response from Google */
    if ($status == "NOT_FOUND") {echo "<center><h4>The postcode you have entered ($postcode2) is not recognised. Please check and try again.</h4></center><br>";}
    if ($status == "ZERO_RESULTS") {echo "<center><h4>The postcode you have entered ($postcode2) is not recognised. Please check and try again.</h4></center><br>";}
    
    /* Google returns UK addresses in a format which includes the electoral district. The electoral district is not a real part of an address and is often a duplicate of the town name. The following code strips it out of the address displayed to the user. Pointless outside the UK although you may be able to modify it to suit if Google does something similar in your location */
    $addy = substr_replace($addy,'', -4);
    $address = preg_split("/,/", $addy);
    $councilpc = array_pop($address);
    $arrlength = count($address);
    $justpc = preg_split("/ /", $councilpc);
    $pclength = (count($justpc) - 1);
    $pc1 = ($pclength - 1);
    
    /* Code to calculate a surcharge amount, change this to suit your needs. As written it will return no surcharge for journeys of 40 minutes or below. After this point the surcharge increases by £10 for every 10 minutes additional travelling time. VAT (sales tax) is then added to the surcharge amount */
    $surcharge = (round($tomin, -1, PHP_ROUND_HALF_UP) - 30);
    $surcharge = ($surcharge * 1.2); // Multiplying by 1.2 adds 20% (UK rate of VAT) to surcharge amount. Change figure to suit.
    
    /* Displays the address returned by Google for the javascript device location or postcode input by the user followed by the surcharge amount if any. Above 120 minutes travelling time it will say the location is too far away, change this to suit. */
    if (empty($tomin)) {
        echo " ";
    } elseif ($tomin < "41") {
        echo "<center><h4>";
    for($x = 0; $x < $arrlength; $x++) {
        echo "$address[$x], ";
    } if (strlen($justpc[$pc1]) < "5") {
    echo "$justpc[$pc1] $justpc[$pclength]";
    } else {
    echo "$justpc[$pclength]";
    }
    echo "</h4><br><br><h4>is within our normal coverage area.</h4><br><br><h5>No surcharge to attend your location.</h5></center>";
    } elseif ($tomin < "121") {
        echo "<center><h4>";
    for($x = 0; $x < $arrlength; $x++) {
        echo "$address[$x], ";
    } if (strlen($justpc[$pc1]) < "5") {
    echo "$justpc[$pc1] $justpc[$pclength]";
    } else {
    echo "$justpc[$pclength]";
    }
    echo "</h4><br><br><h4>is outside our normal coverage area.</h4><br><br><h5>There would be a surcharge of £";
    echo $surcharge;
    echo " to attend your location</h5></center>";
    } else {
        echo "<center><h4>Sorry, we do not cover<br><br>";
    for($x = 0; $x < $arrlength; $x++) {
        echo "$address[$x], ";
    } if (strlen($justpc[$pc1]) < "5") {
    echo "$justpc[$pc1] $justpc[$pclength]";
    } else {
    echo "$justpc[$pclength]";
    }
    echo "</h4></center>";
    }
    
    /* Uses width of users browser window to request high or low res map image from Google */
    if ($_POST['width'] <= '768') {
    $mapres = '320x160';
    } else {
    $mapres = '640x320';
    }
    
    /* Inserts the map image. Change the values in caps to your own details. Mine uses custom markers but these are optional. See Google static maps API for more info */
    if ($status == "OK") {
    echo '<div class="Flexible-container">';
    echo '<img src="https://maps.googleapis.com/maps/api/staticmap?&size=' . $mapres . '&scale=2&markers=icon:http://URL-OF-CUSTOM-MARKERZ%7CYOUR-LOCATION-FOR-MARKER-PLACEMENT&markers=icon:http://URL-OF-CUSTOM-MARKER-FOR-DESTINATION%7C' . $postcode1 . '&key=ENTER-GOOGLE-MAP-API-KEY">';
    echo '</div>';
    }
     /* This stops the site sending push notifications and emails if Wordpress admin user is logged in. So I can check for surcharge amounts without getting loads of emails through. Pretty sure using goto is frowned upon as a big PHP no-no, but I'm a noob who doesn't know any better */
    if ( is_admin_bar_showing() ) {
    goto noemail;
    }
    
    if (!empty($tomin)) {
    if ($tomin < "41") {
    $sur = 'No surcharge';
    } elseif ($tomin < "121") {
    $sur = '£' . $surcharge . ' surcharge';
    }
    else {
    $sur = 'Too far away';
    }
    }
    
    /* Sends surcharge info to your mobile as a Pushover notification, so you know what users have been quoted. You need the app and a free API key. See http://www.pushover.net for more info. Change values in caps to match your token and user values. */
    if (!empty($tomin)) {
    $url = 'https://api.pushover.net/1/messages.json';
    $body = 'token=PUSHOVER-API-TOKEN&user=PUSHOVER-API-USER-KEY&title=Distance+quote+for+' . strtoupper($postcode1) . '&message=' . urlencode(ceil($tomin)) . '+minutes.+' . urlencode($sur) . '+' . urlencode($addy) . '&sound=tugboat&url=http://maps.google.com/maps?hl=en%26q=' . strtoupper($postcode1) . '&url_title=' . urlencode($_SERVER['REMOTE_ADDR']) . '';
    $response = wp_remote_post( $url, array(
    	'method' => 'POST',
    	'timeout' => 45,
    	'redirection' => 5,
    	'httpversion' => '1.0',
    	'blocking' => true,
    	'headers' => array("Content-type" => "application/x-www-form-urlencoded"),
    	'body' => $body,
    	'cookies' => array()
        ) );
    }
    
    /* Emails you surcharge info so you have a record of what your users have been quoted. Change email address to your own. */
    if (empty($tomin)) {
    } elseif ($tomin < "41") {wp_mail( 'your@email.com', 'Distance quote for ' . strtoupper($postcode1) . '', '' . $addy . ' ' . ceil($tomin) . ' minutes. No surcharge. http://maps.google.com/maps?hl=en&q=&#039; . strtoupper($postcode1) . ' Client IP = ' . $_SERVER['REMOTE_ADDR'] . '', 'From: "Website Distance Quote" <your@email.com>' );
    } elseif ($tomin < "121") {wp_mail( 'your@email.com', 'Distance quote for ' . strtoupper($postcode1) . '', '' . $addy . ' ' . ceil($tomin) . ' minutes. £' . $surcharge . ' surcharge. http://maps.google.com/maps?hl=en&q=&#039; . strtoupper($postcode1) . ' Client IP = ' . $_SERVER['REMOTE_ADDR'] . '', 'From: "Website Distance Quote" <your@email.com>' );
    } else {wp_mail( 'your@email.com', 'Distance quote for ' . strtoupper($postcode1) . '', '' . $addy . ' ' . ceil($tomin) . ' minutes. Outside coverage area. http://maps.google.com/maps?hl=en&q=&#039; . strtoupper($postcode1) . ' Client IP = ' . $_SERVER['REMOTE_ADDR'] . '', 'From: "Website Distance Quote" <your@email.com>' );
    }
    
    /* Displays travelling time and surchage amounts below the map only if user is logged in as WP Admin. */
    noemail:
    if ( is_admin_bar_showing() ) {echo "<br>Travelling time is $tomin minutes. Surcharge amount is £$surcharge<br><br>";}

Thank NAME, for their help.

Let NAME know exactly why they deserved these points.

Gift a custom amount of points.