=== PeakMaps ===
Contributors: consigno
Tags: maps, leaflet, hiking, gpx, peaks
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 2.9.77
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html

Leaflet map with GPX tracks, markers, elevation profile, and its own filterable peak database with table and Gutenberg blocks.

== Description ==

Turn your list of summits, trails, and viewpoints into a beautiful, interactive map — no coding required.

PeakMaps is a WordPress plugin built for hiking blogs, outdoor clubs, tourism sites, and anyone who wants to showcase peaks, trails, and points of interest on a real, interactive map. Add a searchable database of locations, complete with GPX tracks and elevation profiles — then drop it into any page or post with a Gutenberg block or shortcode.

PeakMaps is **completely free**, with no peak-count cap, no expiring features, and no upgrade popups. **PeakMaps Pro** is a separate, optional add-on for sites that want Norwegian topographic maps (Kartverket), custom marker styles, extra data fields, on-map tools, GPX downloads, and bulk CSV import/export.

[See it in action: demo table](https://fjellturblogg.no/fjelltopper) | [demo map](https://fjellturblogg.no/kart)

= What PeakMaps does =

Add peaks to your own dedicated database, attach GPX tracks, and PeakMaps takes care of the rest: rendering them on interactive Leaflet maps, generating a sortable and filterable table, calculating elevation profiles — all without a single line of code. Place as many maps and tables as you like, anywhere on your site, each with its own filters and settings.

= 🗺️ Interactive map =

* Built on **Leaflet** — fast, lightweight, and mobile-friendly.
* **Marker clustering** keeps busy maps clean at lower zoom levels.
* Background layers: **OpenStreetMap** and **OpenTopoMap**, with an easy switcher for visitors.
* **Fullscreen mode** for an immersive map view.
* Smooth touch and pinch-zoom on mobile.

= 🏔️ GPX tracks and elevation profiles =

* Attach a GPX track to any peak and it renders on the map automatically — the map zooms to show the whole track.
* Map and profile work together: point at the track on the map to see that spot in the elevation profile, or click the profile to zoom the map there.
* Optional **distance markers and direction arrows** along the track, switched on and off by visitors with a «km» button on the map.
* **Elevation profile charts** with interactive hover tooltips — elevation and distance at any point along the track.
* Ascent/descent totals, min/max elevation, total distance calculated automatically, plus an optional **estimated walking time**.
* Optionally **coloured by steepness** (0–5 % up to 35 %+), with the gradient in the hover tooltip; **start and finish** are marked on the map.
* **Secure GPX proxy** built in — tracks hosted anywhere load smoothly without CORS issues.
* Each track's **length and total ascent are stored with the peak**, and the admin warns you when a GPX file can't be read or never comes near its peak (usually the wrong file).

= 📊 Smart, filterable table =

* Fully **sortable and searchable** — sort by name, elevation, track length, ascent, municipality, county, visited date, and more.
* **Live filtering** by municipality, county, elevation range, track length (e.g. hikes under 10 km), free-text search, and visited status — no page reload.
* **Pagination** with configurable results per page.
* Optional row-number column and configurable column selection per instance.

= 🗄️ Your own peak database =

* A dedicated, purpose-built database — not repurposed WordPress posts — for fast, clean querying at any scale, with no upper limit.
* **Visited/completed tracking** — doubles as a personal or club peak-bagging log.
* Show/hide individual peaks without deleting their data.
* Full admin interface: search, filter, sort, bulk-edit, bulk-delete, and duplicate entries.

= 🌍 Built for real-world use =

* Works via **Gutenberg blocks** (with live editor preview) or classic **shortcodes** (`[peakmaps_map]` and `[peakmaps_table]`).
* **Norwegian Bokmål translation** included; standard WordPress i18n for adding more languages.
* Automatic **cache clearing** on settings changes, compatible with WP Rocket, W3 Total Cache, WP Super Cache, LiteSpeed Cache, and WP-Optimize.
* Assets load only on pages that actually contain a map or table.

= PeakMaps Pro =

[PeakMaps Pro](https://consigno.no/peakmaps) is a separate, optional add-on that extends the free plugin without removing anything from it. Pro adds:

**🇳🇴 Norwegian Kartverket maps**
Norway's official topographic map, raster map, and nautical chart as additional background layers. When a Kartverket layer is active, Pro also refines elevation profiles against Kartverket's own terrain model for extra accuracy.

**🎨 Custom marker styles**
Choose from circular, square, triangular, diamond, or star markers in any colour — or upload your own custom marker image.

**📝 Five custom extra fields**
Add up to five fields with labels you choose — difficulty, parking notes, permit info, or anything your project needs. Supports wildcards (`*`, `?`), exclusion (`!`), and numeric comparisons (`>`, `<`, `>=`, `<=`) in filters.

**🛠️ On-map tools**
A distance-measuring tool and a «find my position» GPS button for visitors browsing on mobile.

**⬇️ GPX download**
Let visitors download GPX files, with an optional terms/consent dialog.

**📥 CSV import and export**
Bulk-load or update peaks from a spreadsheet, with insert mode (add new entries) and update mode (fill in missing fields on existing ones). Export your full peak database to CSV at any time — perfect for backups or editing in Excel.

[Find out more about PeakMaps Pro →](https://consigno.no/peakmaps)

= Perfect for =

* Hiking and outdoor blogs documenting summits, trails, and viewpoints
* Tourism boards and destination sites showcasing regional attractions
* Hiking clubs maintaining a shared, browsable peak list
* Personal peak-bagging logs for trackers working through a summit list

= External services =

This plugin connects to external services to display maps, elevation profiles, and GPX tracks. Details for each service are below.

**Map tiles (loaded directly by the visitor's browser)**

When a map is shown, the visitor's browser requests map tile images (small square map images) directly from one or more of the following providers, depending on which background map is selected. Only standard tile-image requests are sent (map coordinates, zoom level, and the browser's normal request headers such as IP address and user agent) — no personal data, peak data, or site content is transmitted beyond what a normal image request contains.

* **OpenStreetMap** (tile.openstreetmap.org) — open, community-maintained world map. Terms: https://operations.osmfoundation.org/policies/tiles/ — Privacy: https://wiki.osmfoundation.org/wiki/Privacy_Policy
* **OpenTopoMap** (tile.opentopomap.org) — open topographic map based on OpenStreetMap and elevation data. Terms/usage: https://opentopomap.org/about#verwendung

**GPX file proxy (server-side request)**

The server also fetches GPX track files, via a proxy, from the URLs stored on each peak (so the file can be shown on a map without cross-origin browser restrictions). The proxy only fetches URLs that exactly match a `gpx_url` already saved on a peak in this site's own database; no other URL can be requested through it. Where a GPX file is hosted determines which third party (if any) receives that request — this is set by the site owner when adding a peak, not by this plugin's own servers.

== Source Code ==

This plugin's own frontend script, `assets/js/nu_script.js`, is shipped as a whitespace-minified build for faster loading. Its full human-readable source — same code, same behavior, just formatted — is included in this plugin at `assets/src/nu_script.js`. No bundler, transpiler, or other build step is used beyond whitespace minification; regenerating the minified file from the source is a single pass through any standard JS minifier.

All other JavaScript and CSS files written for this plugin (everything under `assets/js/` and `assets/css/` not listed below) are already shipped in their normal, unminified, human-readable form — no separate source is needed for those.

Bundled third-party libraries (Leaflet, Leaflet.markercluster, Leaflet.fullscreen, Leaflet.UTM) are minified as distributed upstream; each file includes a header comment with its name, license, and source repository URL. GPXParser.js (MIT) is included in readable form with a few PeakMaps fixes, listed in its header comment.

== External services ==

This plugin's map displays background map tiles from a small set of publicly available tile providers, selectable per-site under PeakMaps → Settings → Background maps. Each provider's tiles are only loaded when the site owner has selected that specific layer to be shown on the map — no data is sent to any of them unless a map using that layer is actually displayed to a visitor. The data sent in every case is limited to what any map-tile request needs: the visitor's browser requests image tiles for the map area currently in view (an approximate geographic tile coordinate, not the visitor's precise location), the same as loading any other embedded map.

* **OpenStreetMap** — the default background map. Tiles are requested from `tile.openstreetmap.org`. [Terms of use](https://operations.osmfoundation.org/policies/tiles/) · [Privacy policy](https://wiki.osmfoundation.org/wiki/Privacy_Policy)
* **OpenTopoMap** — a topographic map style built on OpenStreetMap data. Tiles are requested from `tile.opentopomap.org`. [Usage policy](https://opentopomap.org/about) · [OpenStreetMap privacy policy](https://wiki.osmfoundation.org/wiki/Privacy_Policy) (OpenTopoMap does not publish a separate one)
* **Esri** — satellite and topographic map styles. Tiles are requested from `server.arcgisonline.com`. [Terms of use](https://www.esri.com/en-us/legal/terms/full-master-agreement) · [Privacy policy](https://www.esri.com/en-us/privacy/overview)
* **CARTO** — light and dark map styles. Tiles are requested from `basemaps.cartocdn.com`. [Terms of use](https://carto.com/legal/) · [Privacy policy](https://carto.com/privacy/)

== Installation ==

1. Upload the `peakmaps` folder to `/wp-content/plugins/`, or install the ZIP via Plugins > Add New > Upload Plugin.
2. Activate the plugin from the Plugins menu in WordPress.
3. Go to PeakMaps in the admin menu to set up map defaults and add peaks.
4. Add a map or table with the "PeakMaps" Gutenberg blocks, or the shortcodes `[peakmaps_map]` / `[peakmaps_table]`.

== Frequently Asked Questions ==

= How do I add peaks? =
Use the "New peak" button under the PeakMaps menu to add peaks one at a time — name, position, GPX track, and the other details.

= How do I display the map or table? =
Use the Gutenberg blocks, or the shortcodes `[peakmaps_map]` and `[peakmaps_table]`. Parameters are documented on the Help page in admin.

= The plugin uses a caching plugin — why don't my changes show up? =
The page cache may serve old HTML. PeakMaps clears the cache automatically when you save the map settings, but also remember to hard-refresh your browser.

= Do I have to pay to use PeakMaps? =
No. PeakMaps is completely free, with no locked features, limits, or license requirements.

== Screenshots ==

1. Interactive map with markers and elevation profile.
2. Sortable and filterable peak table.
3. Admin: map settings.

== Changelog ==

= 2.9.77 =
* On an overview map with several tracks and clustering on, the centre peak's track now clusters like the others: it shows when the peak's marker stands on its own and is hidden while the marker is part of a cluster. On a peak's own map (a single track), or with `fit="yes"`, the track is still always shown.

= 2.9.76 =
* Start and finish markers are now only drawn on maps with a single GPX track. Overview maps with several tracks no longer show them.

= 2.9.75 =
* Fixed: since 2.9.70, a map with a centre peak and a fixed zoom level zoomed in to that peak's track even when the map showed many peaks and tracks. The map now only zooms to fit the track when it shows a single GPX track; maps with several tracks keep the centre peak and start zoom. New `fit="yes"` forces zoom-to-track on such maps, and `fit="no"` turns it off everywhere.
* Fixed: when the high-resolution ("F") variant of a GPX file is not a usable track, the map falls back to the normal file.

= 2.9.74 =
* New: map → profile. Pointing at the track on the map shows that spot in the elevation profile (a tap on a phone does the same); clicking the profile zooms the map to that point.
* New: a «km» button on the map shows distance markers (every 1 km; 0.5 to 10 km depending on the length) and arrows in the walking direction. Off by default; each visitor's choice is remembered. Markers that would overlap are hidden, in order along the track, so an out-and-back hike stays readable.

= 2.9.73 =
* Steepness colours and the estimated walking time in the elevation profile are now off by default. Switch them on under PeakMaps → Settings → Show / hide.

= 2.9.72 =
* New: estimated walking time (Naismith's rule: 5 km/h plus 1 hour per 600 m of ascent) next to the elevation profile's totals.
* New: the elevation profile is coloured by steepness, measured over 100 m or more so GPS noise doesn't show; a legend is shown and the hover box gives the gradient.
* New: start and finish markers on the map's own track (one marker for a round trip). All three can be switched off under Settings → Show / hide.
* Fixed: the peaks table's filters, sorting and paging did nothing for logged-in visitors, and its REST request always failed (WordPress read its "_wpnonce" parameter as its own cookie nonce).
* All inline onclick/onchange handlers removed from the admin and the peaks table; events are bound in the scripts instead.

= 2.9.71 =
* Fixed: "The link does not point to a readable GPX file" could be shown for every peak. With the high-resolution GPX setting on, an «F» variant that answered with a web page stopped the normal file from being tried; files on the site itself are now always read from disk (also outside the uploads folder), never fetched over HTTP, where some hosts' bot protection returns a challenge page instead of the file; and servers without PHP's XMLReader extension can read GPX files too. All peaks are recalculated automatically.
* The warning now says what the server actually got (HTTP status, web page or not, the first characters), so a remaining problem can be pinned down.

= 2.9.70 =
* New: a peak's map zooms to show its whole GPX track once loaded (unless the visitor has already moved the map). `fit="no"` on `[peakmaps_map]` keeps the start zoom.
* New: track length and total ascent are worked out from each peak's GPX file and stored. Peaks table: new `length` and `ascent` columns, sorting, `min_length`/`max_length` (km) and a length filter field. Existing peaks are calculated automatically the next time the admin peaks list is opened.
* New: the admin warns when a GPX file can't be read or never comes near its peak, in the list (⚠) and when saving.
* Fixed (block themes): the peaks table's filters, sorting and paging did nothing, and maps could load without their GPX tracks.
* Fixed: the track of the map's own peak disappeared when its marker joined a cluster.
* Fixed: a database error on every plugin upgrade; the table's sort arrow showed the wrong column; a duplicate "per page" selector in the admin.

= 2.9.69 =
* Peaks list in the admin: clicking a peak's name now opens it for editing, like other WordPress admin lists. The peak's own post, when it has one, opens in a new tab from a small icon after the name. The separate edit button at the end of each row is gone.

= 2.9.68 =
* The map now passes the peak's registered height (`hoyde_m`) to the browser, and the elevation profile offers add-ons a hook for the value shown as ▲. PeakMaps Pro uses it for an optional "Summit height" setting. No visible change without an add-on.

For older versions, see CHANGELOG.md in the plugin's files (this section is trimmed to stay under WordPress.org's 5000-character readme changelog limit).
