Coder Social home page Coder Social logo

birgire / geo-query Goto Github PK

View Code? Open in Web Editor NEW
66.0 5.0 11.0 98 KB

WordPress plugin: Geo Query - Modify the WP_Query/WP_User_Query to support the geo_query parameter. Uses the Haversine SQL implementation by Ollie Jones. With a Rest API example and support for an existing custom table .

License: MIT License

PHP 89.29% Shell 10.71%
wp-query wordpress distance geo-query wordpress-plugin haversine-formula

geo-query's Introduction

WordPress plugin: Geo Query

Build Status GitHub license Packagist

Description

This plugin adds a support for the geo_query part of the WP_Query and WP_User_Query.

Supports geo data stored in post/user meta or in a custom table.

It uses the Haversine SQL implementation by Ollie Jones (see here).

The plugin works on PHP 5.3+.

It supports the GitHub Updater.

Activate the plugin and you can use the geo_query parameter in all your WP_Query and WP_User_Query queries.

Few examples are here below, e.g. for the Rest API.

Installation

Upload the plugin to the plugin folder and activate it.

To install dependencies with Composer (not required):

composer install

or

php composer.phar install

within our folder. See here for more information on how to install Composer.

Then play with the example below, in your theme or in a plugin.

Have fun ;-)

Example - Basic WP_Query usage:

Here's an example of the default input parameters of the geo_query part:

$args = [
    'post_type'           => 'post',    
    'posts_per_page'      => 10,
    'ignore_sticky_posts' => true,
    'orderby'             => [ 'title' => 'DESC' ],
    'geo_query'           => [
        'lat'                =>  64,                                // Latitude point
        'lng'                =>  -22,                               // Longitude point
        'lat_meta_key'       =>  'geo_lat',                         // Meta-key for the latitude data
        'lng_meta_key'       =>  'geo_lng',                         // Meta-key for the longitude data 
        'radius'             =>  150,                               // Find locations within a given radius (km)
        'order'              =>  'DESC',                            // Order by distance
        'distance_unit'      =>  111.045,                           // Default distance unit (km per degree). Use 69.0 for statute miles per degree.
        'context'            => '\\Birgir\\Geo\\GeoQueryHaversine', // Default implementation, you can use your own here instead.
    ],
];
$query = new WP_Query( $args );

Example - Rest API usage:

Here's a modified example from @florianweich:

add_filter( 'rest_query_vars', function ( $valid_vars ) {
	return array_merge( $valid_vars, [ 'geo_location' ] );
} );

add_filter( 'rest_post_query', function( $args, $request ) {
	$geo = json_decode( $request->get_param( 'geo_location' ) );
	if ( isset( $geo->lat, $geo->lng ) ) {
		$args['geo_query'] = [
			'lat'                =>  (float) $geo->lat,
			'lng'                =>  (float) $geo->lng,
			'lat_meta_key'       =>  'geo_lat',
			'lng_meta_key'       =>  'geo_lng',
			'radius'             =>  ($geo->radius) ? (float) $geo->radius : 50,
		];
	}
	return $args;
}, 10, 2 );

Test it with e.g.:

https://example.com/wp-json/wp/v2/posts?geo_location={"lat":"64.128288","lng":"-21.827774","radius":"50"}

One can use rest_{custom-post-type-slug}_query filter for a custom post type.

Example - Basic WP_User_Query usage:

Here's an example from @acobster:

$args = [
  'role'      => 'subscriber',
  'geo_query' => [
    'lat'          => 47.236,
    'lng'          => -122.435,
    'lat_meta_key' => 'geo_lat',
    'lng_meta_key' => 'geo_lng',
    'radius'       => 1,
    'context'      => '\\Birgir\\Geo\\GeoQueryUserHaversine',
  ],
];

$query = new WP_User_Query( $args );

Example - Basic WP_Query usage for fetching lat/lng data from a custom table with Haversine formula:

$args = array(
   	'post_type'           => 'post',    
   	'posts_per_page'      => 10,
   	'ignore_sticky_posts' => true,
   	'orderby'             => array( 'title' => 'DESC' ),		    
	'geo_query' => array(
		'table'         => 'custom_table', // Table name for the geo custom table.
		'pid_col'       => 'pid',          // Column name for the post ID data
		'lat_col'       => 'lat',          // Column name for the latitude data
		'lng_col'       => 'lng',          // Column name for the longitude data 
		'lat'           => 64.0,           // Latitude point
		'lng'           => -22.0,          // Longitude point
		'radius'        => 1,              // Find locations within a given radius (km)
		'order'         => 'DESC',         // Order by distance
		'distance_unit' => 111.045,        // Default distance unit (km per degree). Use 69.0 for statute miles per degree.
		'context'       => '\\Birgir\\Geo\\GeoQueryPostCustomTableHaversine', // Custom table implementation, you can use your own here instead.
	),
);

$query = new WP_Query( $args );

Check the unit test method test_custom_table_in_wp_query() as a more detailed example.

Notes on the parameters:

  • The plugin assumes we store the latitudes and longitudes as custom fields ( post meta), so we need to tell the query about meta keys with the 'lat_meta_key' and 'lng_meta_key' parameters.

  • Skipping the 'radius' parameter means that no distance filtering will take place.

  • If we use the 'order' parameter within the 'geo_query', then it will be prepended to the native 'orderby' parameter.

  • The 'distance_unit' parameter should be 69.0 for distance in statute miles, else 111.045 for distance in kilometers.

  • If we want to use the optimized Haversine version by Ollie Jones, we use:

      'context' => '\\Birgir\\Geo\\GeoQueryHaversineOptimized'
    

    Notice that on our current plugin setup (i.e. fetching data from the LONGTEXT post meta fields) this isn't more performant than the default GeoQueryHaversine class.

    A future work could be to use a custom table with indexes, for the optimization to work.

  • If we create our own implementation of the Haversine formula, for example the GeoQueryCustom class, we just have to make sure it implements the GeoQueryInterface interface:

      'context' => 'GeoQueryCustom'
    

Feedback

Any suggestions are welcomed.

Changelog

0.2.2 (2024-01-05)

  • Bump composer/installers to ^2.0.0. Props @wujekbogdan

0.2.1 (2023-07-26)

  • Fix deprecated notice in PHP 8.1 #24

0.2.0 (2020-04-25)

  • Support for fetching points from a custom table and doing Haversine formula in WP_Query.

0.1.1 (2019-01-23)

  • Fixed #14. Fixed the user query ordering. Props @baden03

0.1.0 (2018-08-06)

  • Added support for user queries. Props @acobster
  • Fixed Travis issue. Travis runs successfully for PHP 7.2, 7.1,7,5.6 when installed via Composer and also 5.4 when installed without Composer. Skip the 5.4 Composer check for now.

0.0.7 (2018-06-27)

  • Fixed #10. Use ^1.0.0 for composer installer. Props @wujekbogdan

0.0.6 (2017-11-16)

  • Fixed #6. Support floating point radius. Props @wujekbogdan
  • Added integration tests.

0.0.5 (2017-02-26)

  • Added fallback for those that don't use Composer
  • Removed the vendor directory

0.0.4 (2017-02-26)

  • Fixed #4. Props @billzhong .

0.0.3 (2015-04-29)

  • Fixed #2. Fixed a typo. Props @Ben764 and @con322.

0.0.2 (2015-03-10)

  • Added: Support for the GitHub Updater.
  • Updated: README.md
  • Changed: Use distance_value instead of distance_in_km in SQL, since we can use miles by changing the distance_unit parameter.

0.0.1 - Init

geo-query's People

Contributors

billzhong avatar birgire avatar wujekbogdan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

geo-query's Issues

404 Error in get_pre_posts Callback

I am getting 404 error. How do i get this to work correctly? Am I doing something incorrect? See images attached for details. please Help!!

function filter_queryby_currentcity( $query ) {
if ( !is_admin() )
{
getGeoipDetails();
//print_r("filter:".trim(getGeoipDetails()["city"]));
// print_r("IP Based geo details".microtime(true).":".$GLOBALS['location_details']['geolat']);

$query->set( 'geo_query', [
  'lat'                =>  $GLOBALS['location_details']['geolat'],                                // Latitude point
    'lng'                =>  $GLOBALS['location_details']['geolong'],                               // Longitude point
   'lat_meta_key'       =>  '_tmbu_lat',                         // Meta-key for the latitude data
   'lng_meta_key'       =>  '_tmbu_lng',                         // Meta-key for the longitude data 
   'radius'             =>  50,                               // Find locations within a given radius (km)
   'order'              =>  'DESC',                            // Order by distance
   'distance_unit'      =>  111.045,                           // Default distance unit (km per degree). Use 69.0 for statute miles per degree.
   'context'            => '\\Birgir\\Geo\\GeoQueryHaversine' // Default implementation, you can use your own here instead.

post_meta_tabe
print_rquery
]);

Retrieve distance information for an item

Hi, is there any way to retrieve information about the distance between two coordinates, eg "3.4 km" between Coordinate A & Coordinate B? It would be nice to display this to a user who is seeing results ordered by distance.

So far looking at the code, that value might be saved in distance_value but I think a function of some kind is required to extract it for display in a loop of items.

GeoQueryUserHaversine returns 0 results when order argument is used

Not sure if the document needs to be updated (see: #7) or if there is a bug, but using the order parameter returns zero results when using GeoQueryUserHaversine. Omitting the order parameter returns as expected.

'geo_query' => [
		'lat'       	       	=> $data['lat'],
		'lng'          	=> $data['lng'],
		'lat_meta_key'  => 'lat',
		'lng_meta_key' => 'lng',
		'radius'       	=> 10,
		'order'       	=> 'DESC',
		'context'       	=> '\\Birgir\\Geo\\GeoQueryUserHaversine',
	 ],

Deprecated notice in PHP 8.1

Deprecated: Creation of dynamic property Birgir\Geo\GeoQueryContext::$db is deprecated in /path/wp-content/plugins/geo-query/src/GeoQueryContext.php on line 26

pre_get_posts not working

function override_search_query( $query ) {
	if ( ! is_admin() && $query->is_search() ) {
			$query->set( 'geo_query', array(
				'lat'                =>  $_GET['latitude'],       
				'lng'                =>  $_GET['longitude'],             
				'lat_meta_key'       =>  'hb_opts_lat_coordinate',                  
				'lng_meta_key'       =>  'hb_opts_long_coordinate',                       
				'order'              =>  'DESC',                           
				'distance_unit'      =>  111.045,                          
				'context'            => '\\Birgir\\Geo\\GeoQueryHaversine', 
			) );
		}
}
add_action( 'pre_get_posts', 'override_search_query' ); 


What is wrong ?

Add support for pre_get_posts

Sample code:

add_action( 'pre_get_posts', 'override_main_query' );

function override_main_query( $query ) {
    if ( $query->is_search && $query->is_main_query() ) {
        $query->set('geo_query', array(
	        'lat'                =>  get_lat_long('lat'),                               
	        'lng'                =>  get_lat_long('long'),                            
	        'lat_meta_key'       =>  'latitude',                         
	        'lng_meta_key'       =>  'longitude',                         
	        'radius'             =>  150,                               
	        'order'              =>  'ASC',                          
	        'distance_unit'      =>  111.045,                         
	    ));
    }
}

This currently does not work. Any help would be appreciated.

WordPress database error: [Unknown column 'settings.distance_unit' in 'field list']

Hey, Awesome plugin.
I've used your geo query for custom post type and it works wonderfully good.
But when I'm trying to use it wp_user_query it thows the following error.
Please help.
Thank you.

Notice: Undefined index: join in /home/abc/public_html/wp-content/plugins/geo-query-master/src/GeoQueryHaversine.php on line 77

Notice: Undefined index: groupby in /home/abc/public_html/wp-content/plugins/geo-query-master/src/GeoQueryHaversine.php on line 80
WordPress database error: [Unknown column 'settings.distance_unit' in 'field list']
SELECT SQL_CALC_FOUND_ROWS wp_users.*, settings.distance_unit * DEGREES( ACOS( COS( RADIANS( settings.latpoint ) ) * COS( RADIANS( mtlat.meta_value ) ) * COS( RADIANS( settings.longpoint ) - RADIANS( mtlng.meta_value ) ) + SIN( RADIANS( settings.latpoint ) ) * SIN( RADIANS( mtlat.meta_value )))) AS distance_value FROM wp_users INNER JOIN wp_usermeta ON ( wp_users.ID = wp_usermeta.user_id ) WHERE 1=1 AND ( ( ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"customer\"%' ) ) ) ORDER BY user_login ASC LIMIT 0, 2

Installation within a theme

Hi just wondering if this can be installed within a theme is it as simple as just using require once in function with the path to geo-query.php?

Also when do a search is it as simple as?

    'lat'                =>  $POST['current_location_lat'],                                // Latitude point
    'lng'                =>  $POST['current_location_lng'],                               // Longitude point
    'lat_meta_key'       =>  'my_lat',                          // Meta-key for the latitude data
    'lng_meta_key'       =>  'my_lng',                          // Meta-key for the longitude data 
    'radius'             =>  $POST['radius'], 

geo-query is not compatible with composer/installers newer than ~1.0.0

I install WordPress with Composer (thank's to the awesome Bedrock tool). Bedrock already uses composer/installers v ^1.4. geo-query depends on composer/installers too, it requires version ~1.0.0 that causes a conflict.

When trying to add geo-query to my project Composer complains that:

Problem 1
    - Installation request for birgir/geo-query ^0.0.6 -> satisfiable by birgir/geo-query[0.0.6].
    - birgir/geo-query 0.0.6 requires composer/installers ~1.0.0 -> satisfiable by composer/installers[1.0.x-dev, v1.0.0, v1.0.1, v1.0.10, v1.0.11, v1.0.12, v1.0.13, v1.0.14, v1.0.15, v1.0.16, v1.0.17, v1.0.18, v1.0.19, v1.0.2, v1.0.20, v1.0.21, v1.0.22, v1.0.23, v1.0.24, v1.0.25, v1.0.3, v1.0.4, v1.0.5, v1.0.6, v1.0.7, v1.0.8, v1.0.9] but these conflict with your requirements or minimum-stability.

Does it really mean that geo-query requires composer/installers that's not newer than ~1.0.0? Or is it just an oversight in the composer.json file? I suppose that it's safe to upgrade that dependency.

Using in conjunction with multiple meta queries in REST url

Is it possible to use this query in conjunction with other meta queries with a relation of OR?

For example, I need to pull in a bunch of listings based on their lat and long values, but also need to include all listings with a listing_type of 'National' and 'State-wide'.

I currently query the National and State-wide listings via this filter in the url:

'?filter[meta_query][relation]=OR&filter[meta_query][0][key]=state_wide_state&filter[meta_query][0][value]=' + this.state + '&filter[meta_query][1][key]=cause_type&filter[meta_query][1][value]=National'

Is there a way I can use this geo-query as another meta_query or combine them in some way?

Many thanks

Reuben

0 results when radius < 1

When radius is between 0 and 1 then the query doesn't give any results. Is it a bug, or is it a desired behavior?

Move the order parameter out of geo_query part of WP_Query

Instead of:

$args = [
    'geo_query' => array(
        'order' => 'DESC', // Order by distance
    ],
];
$query = new WP_Query( $args );

we should use the ordering support of WP_Query, e.g.

$args = [
    'orderby'      => array( 'distance' => 'ASC', 'title' => 'DESC' ),
    'geo_query' => [... ]
];
$query = new WP_Query( $args );


Setting up but posts displaying as normal or white screen

I am trying to install/setup a query using this plugin :)

So far I have downloaded & activated it and setup my is below

However when using the following nothing really happens all posts display as normal (not the ones within the radius)

$args = array(
'post_type'          => 'lostbox_items',    
'posts_per_page'     => -1,
'orderby'            => array( 'title' => 'DESC' ),
'geo_query' => array(
    'lat'                =>  51.5286416,                                // Latitude point
    'lng'                =>  -0.1015987,                               // Longitude point
    'lat_meta_key'       =>  '_lbx_lat',                          // Meta-key for the latitude data
    'lng_meta_key'       =>  '_lbx_lng',                          // Meta-key for the longitude data 
    'radius'             =>  1,                               // Find locations within a given radius (km)
    'order'              =>  'DESC',                            // Order by distance
    'distance_unit'      =>  111.045,                           // Default distance unit (km per degree)
    'context'            => 'GeoQueryCustom', // Default implementation, you can use your own here      instead.
    ),
);
$query = new WP_Query( $args );

If I use the below I get a white screen below my header

'context'            => '\\Birgir\\Geo\\GeoQueryHaversine'

Debugging it I get this error when using the above line

Catchable fatal error: Object of class wpdb could not be converted to string in /Applications/MAMP/htdocs/website/wp-content/plugins/geo-query-master/src/GeoQueryHaversine.php on line 37

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.