Enhancing WordPress Websites with Geolocation Data
by Duško Angirević Duško Angirević

Enhancing WordPress Websites with Geolocation Data

In today's digitally interconnected world, leveraging geolocation data has become paramount for enhancing user experiences and personalizing content.

WordPress, a versatile platform renowned for its flexibility, empowers developers and site owners to integrate geolocation data as meta values, opening up a realm of possibilities for dynamic content delivery.

This article delves into the strategic utilization of geolocation data within WordPress, highlighting its potential to create location-aware websites. Moreover, it provides insights into structuring queries for optimal organization, ensuring efficient retrieval of geolocation-based information. By embarking on this journey, readers will gain a comprehensive understanding of how geolocation data can be harnessed as a powerful asset within the WordPress ecosystem, transforming websites into tailored, location-responsive platforms.

We had an interesting idea: we all have some unneeded things in our possession but we don’t want to throw them away but if someone would like to have it we would give it to them. It could be a book or piece of clothing. So why not create an app for users to give away their thing to someone who needs them?

Users would create an account, and create giveaways by uploading a few photos, adding tile and description, giving it some category and posting it with geolocation. Other users would be able to see items around them and arrange pickup with the owner.

So item creation - easy. Uploading images - easy. Fetching items around you based on your location, that's the tricky one.

So each item would have io_lng and io_lat meta values pre populated with the owner's location. So how to fetch them?

For this case, we used a custom Wordpress REST route which accepts lat and lng parameters. Just on rest_api_init action initialize route as follows:

add_action('rest_api_init', function () {
register_rest_route('iodata/v1', '/geo', array(
        'methods'   => 'POST',
        'callback'  => ‘cb_iodata_geoposts’,
        'args'      => array(
            'lat1' => array(
                'required' => true,
                'validate_callback' => function ($param, $request, $key) {
                    return is_numeric($param);
                }
            ),
'lat2' => array(
                'required' => true,
                'validate_callback' => function ($param, $request, $key) {
                    return is_numeric($param);
                }
            ),
            'lng1' => array(
                'required' => true,
                'validate_callback' => function ($param, $request, $key) {
                    return is_numeric($param);
                }
            ),
'lng2' => array(
                'required' => true,
                'validate_callback' => function ($param, $request, $key) {
                    return is_numeric($param);
                }
            )
        )
    ));
});

So now we have the REST route ready. But why do we need lat1 and lat2? If you are using some map lat1, lng1 and lat2, lng2 pairs are used to set bounds of viewable map area.

Now we need to pass these parameters but there are few things that need to be done. We will pass these params to the WP query, but the issue is that longitude and latitude are written with a lot of decimal places, and the database will most likely trim it down to two places. So we will intercept SQL and specify precision for decimal places.

function add_decimal_params($sqlarr)
{
    remove_filter('get_meta_sql', 'add_decimal_params');
    $sqlarr['where'] = str_replace('DECIMAL', 'DECIMAL(20,16)', $sqlarr['where']);
    return $sqlarr;
}
add_filter('get_meta_sql', 'add_decimal_params');

And in the end fetching data:

function cb_iodata_geoposts($request)
{
    $args = [
        'posts_per_page'   => -1,
        'meta_query' => [
            [
                'key' => 'io_lng',
                'compare' => '>=',
                'value' => floatval(min($request['lng1'], $request['lng2'])),
                'type' => 'DECIMAL'
            ], [
                'key' => 'io_lng',
                'compare' => '<=',
                'value' => floatval(max($request['lng1'], $request['lng2'])),
                'type' => 'DECIMAL'
            ], [
                'key' => 'io_lat',
                'compare' => '>=',
                'value' => floatval(min($request['lat1'], $request['lat2'])),
                'type' => 'DECIMAL'
            ], [
                'key' => 'io_lat',
                'compare' => '<=',
                'value' => floatval(max($request['lat1'], $request['lat2'])),
                'type' => 'DECIMAL'
            ]
        ]
    ];
    $posts = get_posts($args);
    return [
        'success' => true,
        'items' => $posts
    ];
}

Few notes here:

  • Meta relation is by default AND so all conditions must be met.
  • We are using floatval to get the float value of a variable.
  • Min / Max are used to get start / end bounds of the provided area.

If you find some time, feel free to explore the app that is using this piece of code: DamDas!