Passing HTML Geolocation Lat Lon into a Form

Sometime we have a form that we want to compare our existing GPS coordinates stored in our database with GPS coordinates we have got from our device (notebook & mobile phone). I ve ever been used ip address to get lat/lon but useless due to its inaccuracy.

The idea is we will use just HTML method getCurrentPosition() to get Lat/Lon, then pass it into our form. But how? I will explain it step-by-step.

I will use simple form where any visitor could make check-in to any spot. In the form the visitor should key in his/her current lat/lon taken from their device automatically. The current lat/lon also could be used to see that the current location of a user is within tolerable distance or no.

This tutorial only focus in one _form.php. I will not consider model or controller.

This is a page where you can test the form, you just click orange Check-in button.

The output is like this one

<?php

use Yii;
use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $this yii\web\View */
/* @var $model frontend\models\ClimbingSpotCheckin */
/* @var $form yii\widgets\ActiveForm */

$enkrip = Yii::$app->generik->enkrip($model->spot_id);
?>

<!-- Start -->

<!-- 1 create button to activate the js method -->
<button onclick="getLocation()">Next&raquo;&raquo;</button>

<script>
    // 2 create js function to get lat lon using getCurrentPosition and pass it to the next function
    function getLocation() {
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(redirectToPosition);
        } else {
            x.innerHTML = "Geolocation is not supported by this browser.";
        }
    }

    //3 create function to run url address infused with lat lon result of the above function
    function redirectToPosition(position) {
        window.location='create.html?lat='+position.coords.latitude+'&long='+position.coords.longitude+'&spot_id=<?= $enkrip?>';
    }
</script>

<?php
//4. try to get passed data from no 3 above
$ori_lat = (isset($_GET['lat']))?$_GET['lat']:''; 
$ori_lon = (isset($_GET['long']))?$_GET['long']:''; 

//5. convert it into decimal number from no 4 above
$current_lat = floatval($ori_lat); //lat result of device scanned
$current_lon = floatval($ori_lon); //lon result of device scanned

//6. select lat lon from database
$query = Yii::$app->db2->createCommand('
    SELECT location as location, 
        ST_X(location) AS longitude,
        ST_Y(location) AS latitude
    FROM vri_climbing_spots
    WHERE id = '.$model->spot_id.'
    ')->queryOne();

$spot_lat = $query['latitude']; //lat from database
$spot_lon = $query['longitude'];   //lon from database

?>
<!-- End -->

<div class="climbing-spot-checkin-form">

    <?php $form = ActiveForm::begin(    
        ['options' => ['onsubmit' => "myButton.disabled = false; return true;"]
        ]); ?>

    <?= $form->field($model, 'spot_id')->hiddenInput()->label(false) ?>
    <?= $form->field($model, 'date')->hiddenInput(['value'=>date('Y-m-d')])->label(false) ?>

    <?php 
    //if gps device enable
    if(!empty($ori_lat) && !empty($ori_lon)){

        //7. calculate distance between device coordinates and database coordinates
        $distance = Yii::$app->generik->distance($spot_lat, $spot_lon, $current_lat, $current_lon, 'K');

        /*
        echo '<br>'.$spot_lat; echo '<br>'.$spot_lon; echo '<br>'.$current_lat; echo '<br>'.$current_lon;
        */

        //8. proceed if the distance is less than x km
        if($distance<3){ ?>
                <?= 'You are allowed to check in to the route, you just '.Yii::$app->formatter->format($distance, ['decimal', 2]).' km from the spot';?>
                
                <!-- 9. Do what ever we want -->
                <!-- insert lat lon of device into form -->
                <?= $form->field($model, 'lat')->hiddenInput(['value'=>$current_lat])->label(false) ?>
                <?= $form->field($model, 'lon')->hiddenInput(['value'=>$current_lon])->label(false) ?>

                <?php if(Yii::$app->user->isGuest){?>
                <?= $form->field($model, 'email')->textInput(['maxlength' => true, 'placeholder' => 'Type your valid email address only'])->label('Email Address') ?>
                <?php } ?>

                <div class="form-group">
                <?= Html::submitButton('<i class="fa fa-save fa-lg fa-fw" aria-hidden="true" style="color:white"></i> Submit', ['class' => 'btn btn-success', 'name'=>'myButton']) ?>
                </div>

        <?php }
            else{
                echo 'Sorry, you are not allowed to check in to the route, your distance from the spot is more than '.Yii::$app->formatter->format($distance, ['decimal', 2]).' km'; //
            }
    }

    ?>

    <?php ActiveForm::end(); ?>

</div>

You could see my comment for. I have given serial number from 1 to 9.

  1. Button to enable Location Service.
    This button, which I ve label with Next, will activate js function getLocation(). It will ask user to Allow the browser to acces Location Services. Please tnsure your device has allow to share Location Services in Privacy section.

  2. Device will give the location (lat/lon)
    Once the function getLocation called, it will pass the result coordinates into the redirectToPosition().

  3. Run the target url with resulting lat/lon
    I use similar url address of _form that is why I call ‘…create.html?lat=’+position.coords.latitude+’&long=’+position.coords.longitude+’&spot_id=<?= $enkrip?>’.

Please note that enkrip is only encripted id, you could see it at the top of code.

  1. Got the results
    These are our trial to get result of the js above. Lat/Lon we got is still in STRING type.

  2. Convert into Decimal
    Location originated from device convert into Decimal by floatval(). Conversion will make it possible to do mathematical operation.

  3. Select existingf coordinates from database.
    The coordinate in this case are stored in POINT type, thats why the select procedure is like that.

  4. Calculate Distance between Two Points.

So far we have two points: one from database, and one from device.

We have to calculate distance between the two point using a function ( I got it from geodatasource).

function 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);
        } else if ($unit == "N") {
            return ($miles * 0.8684);
        } else {
            return $miles;
        }
      }

I put the above function in component then make it easy to call just like this Yii::$app->generik->distance(). Of course you can put it any place you like.

  1. Activate other form elements when calculated distance is within x km
    It will prevent any user far from the spot to make a check-in.

  2. Pass the resulting Lat/Lon into the form element.
    Be noticed that lat and lon field must be decimal, I use decimal(18,15).

That’s it. I have tested it and got accuracy between 20-30 meters.
I hope you could catch my explanation and could assist it in your project.
Good luck.

2 Likes