Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/lai-team/bv_geotagged_media/archive/refs/heads/main.zipReads EXIF GPS data from uploaded images and writes it onto posts as GeoJSON, then maintains the per-category "journey" lines that connect them.
This is the producer side of the beauVoyage geo pipeline. Everything the
bv_map plugin renders — marker positions and journey routes — originates
here:
| Meta key | Written by | Read by |
|---|---|---|
geom_point |
bv_attachment() on upload, copied to the post by bv_thumbnailmeta_postmeta() |
bv_map, digital-nomad-child, voyage |
geom_line2__<term_id> |
bv_update_gline2() |
bv_map (journey lines) |
datetime |
bv_attachment(), from EXIF DateTimeOriginal |
bv_map (orders journey legs) |
It also owns $special_categories, a global read by three other codebases.
Requirements
- WordPress multisite
- PHP 7.4+ with the
exifextension - WP-GeoMeta —
WP_GeoUtil::LineString(),st_startpoint(),st_endpoint() voyageplugin —bv_get_adjacent_post()
Both plugin dependencies are now guarded — a missing WP-GeoMeta or voyage
makes the geo features go quiet rather than fatal — but the plugin cannot do
its job without them.
The $special_categories contract
index.php:14-15 declares, at file scope:
global $special_categories;
$special_categories = array('private','standalone','uncategorized');
so it exists only once this plugin has loaded. It is read by:
voyage/voyage.phpandvoyage/includes/tec_hooks.phpbv_map, viabv_map_special_categories()- this plugin, in
bv_update_catline()
There is no load-order guarantee between plugins. It works today because
bv_geotagged_media sorts alphabetically before bv_map and voyage. Both of
those now wrap the read in an isset() guard — bv_map/includes/compat.php
and voyage's bv_special_categories() — precisely because this owner is
unguarded. If this plugin is renamed or deactivated, the global silently
disappears.
Layout
index.php The whole plugin
Fixed in this pass
The initial commit was a faithful import of what is deployed, with the defects recorded rather than fixed. They are fixed now.
- Special categories were never actually excluded.
bv_update_catline()didin_array( $cat, $special_categories_local )where$catis aWP_Termobject and the array holds slug strings, so the comparison never matched and journey lines were generated forprivate,standaloneanduncategorizedtoo. Now compares$cat->slugwith strictin_array(). This changes behaviour on the front end — posts in those three categories will stop contributing to journey lines, which is what the code always meant to do. - EXIF parsing could fatal on upload.
read_gps_location()divided the two halves of each GPS rational without checking the denominator; on PHP 8 that throwsDivisionByZeroErrorand takes down the upload request. All six conversions now go throughbv_gtm_exif_rational(), which returns0.0for a zero or missing denominator. The function also bails before callingexif_read_data()unless the file is a string, theexifextension is loaded, andisImage()says it is an image — previously it ran on every attachment, returnedfalsefor non-images, and then warned on$info['DateTimeOriginal'].datetimeis only returned when EXIF has it. gtm_plugin_main.php_gentritdeleted (696 lines). It was an abandoned predecessor, never loaded because WordPress only executes.phpfiles. It held superseded copies ofread_gps_location,bv_attachment,bv_thumbnailmeta_postmeta,bv_update_gline2,bv_update_catlineandbv_get_nghbpost_geom— renaming it to.phpwould have fatally redeclared about eight functions.isImage()/isVideo()restored at source. They are the only two things from that file anything still wanted:voyage's story-list template calls them, andbv_map/includes/compat.phphad to reimplement them. Both definitions are now behindfunction_exists()on both sides, so whichever plugin loads first wins and no coordinated deploy is needed. The new implementation parses the path withwp_parse_url()before taking the extension, so a URL with a query string (photo.jpg?w=300) is classified correctly where the old substring check failed;WEBPwas added.- Removed the no-op
transition_post_statushook.bv_thumbnailmeta_postmeta()was attached to bothsave_post($post_id, $post, $update) andtransition_post_status($new_status, $old_status, $post). On the latter the first parameter is a status string, so the callback returned immediately — it never did anything. Its re-entrancyremove_action()also omitted the priority 30 it was registered at, so it would not have matched anyway.save_postis unchanged and is the hook that was actually working. $special_cat_idsremoved. It accumulated with+=— union by key — so every element after the first was discarded and it could only ever hold one id. Nothing read it, and it issued threeget_category_by_slug()queries at file scope on every request, front-end and admin.WP_GeoUtilguarded.bv_update_gline2()now returns early unless the class exists, instead of fatalling on every post save when WP-GeoMeta is deactivated. It also keeps the existing line whenLineString()returns empty rather than overwriting good data with nothing — see below.bv_get_adjacent_post()guarded, andbv_get_nghbpost_geom()'s signature reordered so the PHP 8.0 deprecation (optional parameter $get_value declared before required parameter $current_post) no longer fires.WP_DEBUG_LOGis on in production, so that was being written todebug.logon every request.$special_categoriesread defensively in this plugin too, viaisset()is_array(), matching the guardsbv_mapandvoyagealready had.
Known issues
- WP-GeoMeta is hand-patched.
wp-geoutil.php:714addslinestringandpointtoget_capabilities(). A plugin update reverts it, after whichWP_GeoUtil::__callStatic()returns null forLineString()and journey lines quietly stop being written.bv_update_gline2()now preserves the existing line in that case rather than erasing it, but the patch still needs reapplying after any WP-GeoMeta update. $special_categoriesis still an unguarded file-scope global owned by this plugin — see the contract section above. The consumers all guard their reads, so the failure mode is silent rather than fatal.- Several functions read post meta via magic properties
(
$current_post->geom_point). That works —WP_Post::__get()falls back toget_post_meta()— but it is undocumented behaviour and reads like an error. bv_update_gline2()still carries a large block of commented-outerror_log()debugging. Left in place; it documents what was tried against the patched WP-GeoMeta.
License
GPL-2.0-or-later. See LICENSE.
Credits
Lai Consulting Team and Gentrit Biba. Derived from
digfish/geotagged-media.
Previously hosted at gitlab.com/beauvoyage/bv_geotagged_media.