[general]
name=TopoTrail
qgisMinimumVersion=3.22
qgisMaximumVersion=4.99
supportsQt6=True
description=Topographic suitability, access zones and least-cost routes for preliminary trail and field planning, from a single DEM.
version=1.5.5
author=MACIEL, L. S. C.
email=herpetomantiqueira@gmail.com
homepage=https://github.com/LuanCortesM/Topotrail#readme
tracker=https://github.com/LuanCortesM/Topotrail/issues
repository=https://github.com/LuanCortesM/Topotrail
category=Raster
tags=trails,accessibility,protected areas,multicriteria analysis,slope,curvature,DEM,GIS,QGIS,least cost path
icon=logo.png
about=TopoTrail is a QGIS plugin designed to support preliminary planning of new trails, access routes and field corridors in natural areas and protected areas. The tool models topographic suitability using a Digital Elevation Model, slope, curvature, relative topographic risk, suitability classes, potential access zones, suggested least-cost routes and access corridors. Drainage is derived from the DEM itself, and each watercourse the route crosses is listed; roads, pastures, existing trails, land cover and legal restrictions are not detected and should be used as complementary evidence in QGIS. No external Python dependencies: it needs only NumPy and GDAL/OGR, which every QGIS installation ships, and uses SciPy when available. Developed by Luan da Silva Cortes Maciel (MACIEL, L. S. C.) as a product of his master's research in Biodiversity in Protected Areas, associated with the Escola Nacional de Botanica Tropical and Jardim Botanico do Rio de Janeiro, under the supervision of Leandro Freitas. Associated project: Herpeto Mantiqueira.
license=MIT
experimental=False
deprecated=False
hasProcessingProvider=True
changelog=1.5.5 - Progress lines, warnings and errors of the calculation now appear in the language chosen in the window, or in the language of QGIS, in all six languages. Up to 1.5.4 the roughly 170 sentences of the algorithm came out in Portuguese whatever the language. Numbers follow the separators of each language, the option names quoted in the messages are the ones the window shows, and the text values of the crossings layer (type and warning) are translated too; field names are unchanged. The Spanish, French, Japanese and Chinese texts have not yet been reviewed by native speakers. The coordinates of a route point moved to a viable cell are written in metres with one decimal instead of scientific notation.
    1.5.4 - New warning when the route goes around steep terrain. With the default maximum slope of 85 percent the route exists but can go a long way round terrain that the DEM sees as steeper, where the real trail climbs it by cables, steps or switchbacks that a 30 m grid does not resolve. The plugin now also traces the route without the slope limit (rivers above the fordable ceiling and avoided areas still block it) and warns when the route is at least 25 percent and 500 m longer and the shorter one really crosses terrain above the limit. The message gives both lengths and the maximum slope that would allow the shorter route. The route itself does not change. Tested on 101 real trails: the warning appeared on 10, the ones with cable or scramble sections (Angels Landing, Bright Angel, Mist Trail, Masada, Madeira PR1, among others), and on all 10 the route with the relaxed limit comes closer to the real trail. On long mountain routes the route takes up to twice as long to compute.
    1.5.3 - The minimum patch area of the access zones is now automatic by default: 0.05 percent of the analysed area, between 1 and 50 ha (5 ha in a 10,000 ha area, 50 ha from 100,000 ha up). Up to 1.5.2 it was a fixed 50 ha, which in protected areas of about 10,000 ha left the steep middle elevations without zones: in Carnaubas State Park the zones covered 4 of 10 elevation bands, now 9. Tested on 15 strictly protected areas sampled from the Brazilian national register (500 to 300,000 ha): the fixed 50 ha left bands out in 7; the automatic rule fixed 4 of them, created no warning elsewhere and gives the same zones as 50 ha in the large areas. The field shows Automatic; a typed value fixes the area and 0 still turns the filter off. In Processing, automatic is -1.
    1.5.2 - Packaging only. The plugin zip no longer ships the developer's Flake8 configuration file, which made the QGIS plugin repository mark 1.5.0 and 1.5.1 as Validated (configured) instead of Validated. The plugin itself is the same as 1.5.1.
    1.5.1 - The window fits the screen: on laptops with display scaling the Continue button could fall below the taskbar; the window now adapts to the available area and opens centred on QGIS. New Report a problem button, at the bottom right: the user describes what happened and the plugin adds the versions, the last error and the end of the progress log, with the home folder hidden, and sends it by e-mail to the author, copies it, or opens the GitHub bug form already filled in. The About box says how to cite, with the Zenodo DOI, and links to the user guide, the source code and the problem report. Reviewed in the six languages, in light and dark themes. Smaller fixes: the altitude band size has a label, the representativeness advice uses the option name shown in the window, and the fordable ceiling help no longer repeats the drainage card.
    1.5.0 - Tested on 35 real trails, from walking paths to alpine routes (Mount Whitney, Half Dome, Longs Peak, Triglav, Kilimanjaro, Fuji, Kinabalu, Olympus, Huayna Picchu, Pico da Bandeira, Pico Parana) and on the project's field tracks, and reviewed line by line before release. Default maximum slope 85 percent instead of 55: 55 connected only 18 of the 35 trails and 13 of the 17 walking trails; 85 connects all the walking trails and leaves out cable and via ferrata sections, and the message gives the value that would connect them. Drainage extracted from the DEM is on by default and informs the route instead of pushing it away from streams: each crossing is listed with the size of the stream, a stretch where the route follows a stream is reported as such, and a river above the fordable ceiling is still a barrier. Zones keep the stream margins, a sampling habitat. The route duration is Tobler time along the route, without the terrain-preference factor that inflated it by a median 1.39 times. A route that fails no longer takes the zones and the maps with it, and the window says so. New in the log: a representativeness report of the zones (altitude, aspect, wetness), with advice when they miss part of the altitude gradient.
    1.4.1 - Tested on fifteen reliefs around the world (Everest, Aconcagua, Grand Canyon, Svalbard, Dead Sea, Amazon floodplain) and on the project's fifteen field tracks. Flat water: global DEMs (Copernicus, SRTM) flatten the sea, lakes and reservoirs to a constant elevation, which looked like ideal walking terrain and drew the route across a fjord; flat surfaces of at least 0.5 km2 are now water -- the sea always leaves the analysis, lakes leave it when drainage is extracted, and otherwise the route warns when it crosses one. Messages: when the points cannot be connected, the error gives the maximum slope that connects them instead of only asking to raise it; when no cell meets the zone constraints (the default altitude range on a Himalayan DEM, for example), the zones are skipped with a warning and the rasters and the route are still written, instead of the whole run stopping. GEOS no longer prints a ring self-intersection warning for every polygon that touches itself at a vertex.
    1.4.0 - Runs the same on any computer. SciPy is now optional (a NumPy fallback with identical results), so the plugin loads on a Debian/Ubuntu QGIS without python3-scipy; the algorithm works from qgis_process and QGIS Server (initProcessing); the reproducible example and the validation scripts run on Windows, Linux and macOS; continuous integration tests the plugin inside real QGIS 3.22, 3.44 and 4.2. Window: the log was written from the worker thread, which Qt forbids; cancelling ended in an error box and could leave the button locked; unloading left the window alive; choosing a DEM switched GDAL exceptions on for the whole session, and with GDAL older than 3.7 the algorithm did not switch them back. Route: optional alternatives corridor, every cell on a path at most x percent costlier than the optimum, with a slack raster, which shows when two corridors cost almost the same; optional fixed working cell aligned to its own multiples, which makes the route independent of the DEM clip; normalisation limits recorded in the log and fixable; field pace as a parameter. Fixes: walking time printed as 4h60; GDAL Memory driver replaced by MEM on GDAL 3.11+.
    1.3.0 - Corrections found by six independent specialist audits of the code and of the dissertation chapter that describes it, each one reproduced on the project's own field data before being applied. Route correctness: the eight-neighbour A* checked only the destination cell on a diagonal step, never the two corner cells the step goes around, so the route slipped through the vertex where two impassable cells meet. On the Mantiqueira sheet with the fordable ceiling at one square kilometre the route crossed five channels declared impassable, and the log still reported that it crossed no watercourse at all; in the Ceara case it walked across a river of 416 square kilometres of contributing area, which is the only reason that case produced a route. A diagonal step now exists only where there is room to pass outside it. Through the same gap, crossing the drainage diagonally was free, because the ford factor applies to whoever lands on the channel cell: two of the eight real crossings of the canonical scene escaped the field-check list, a quarter of the total. The corner is now charged and detected. When the two points turn out to be joined only by such a vertex, the error says so and says that what is missing is width, instead of suggesting a higher slope limit, which changes nothing. Plan curvature: it was the geometric contour curvature of Moore et al. (1991), which diverges as the gradient goes to zero because it is the curvature of the contour line itself, and the model scores shape by distance from zero -- so the gentlest ground got the worst shape score. Measured on the Mantiqueira scene, the correlation between the logarithm of slope and the shape score was plus 0.58, with the gentlest decile scoring 0.704 against 0.958 for the steepest, and the shape criterion working against the slope criterion under the same weight. It is now the tangential curvature of Mitasova and Hofierka (1993), which the code already cited: the correlation falls to plus 0.06 and the extreme deciles to 0.779 and 0.805. Data safety: writing an output into a GeoPackage that already exists no longer deletes the file. Three user layers vanished without a warning while the algorithm reported success; the layer is now replaced inside the file and everything else stays. Silences that the audit measured, now spoken: a constraint narrower than the pixel says it had no effect; a start or end point moved to the nearest viable cell reports the distance, and warns above 100 metres, the radius of the corridor the plugin itself delivers -- the worst case measured was 7.5 kilometres with no message at all; a criterion raster carrying an undeclared sentinel is named before it becomes a score, where minus 3.4e38 was moving the mean score from 0.656 to 0.984 and changing the route. QGIS conformance: initAlgorithm no longer reads QgsProject.instance(), which is not the user's project during background execution; OUTPUT_FILE is returned in the results, without which the Modeler and batch mode cannot chain; 72 bare exceptions became QgsProcessingException, so the user reads the sentence and not a traceback; gdal.UseExceptions is no longer switched on at import, where it changed the GDAL of the whole QGIS process and left eight friendly error branches unreachable. Also: the legend written into the raster follows the class limits actually requested rather than the factory ones; the memory ceiling covers the path where the user supplies ready-made rasters; the per-cell memory estimate goes from 134 to 230 bytes, measured; roughness stops being printed in metres, being dimensionless; the hydrology decimation samples the centre of each block instead of its corner, which was shifting the extracted network by half a window; and the package is now built from git archive, without the caches that were shipping inside it.
    1.2.0 - Corrections found by an adversarial review of the code against the dissertation chapter that describes it. Accumulated climb: the route's ganho_m field returned max(altitude) - first(altitude), an amplitude, not the climb you actually walk; on a ridge traverse the two differ by nearly a factor of two. It now sums the positive increments, perda_m reports the descent, and the old value stays available as desnivel_max_m. Diagnostic log: it recorded the cost model with a two-way conditional over a three-value enum, so every run in Tobler mode -- the dialog's default -- was logged as the inverse model; and it recorded four of the seven weights, omitting wetness, roughness and the extra raster. It now records all of them plus the cost model, contrast, intermediate destinations, fordable ceiling, drainage threshold, constraint mode and vertical unit, so a third party can reproduce a run from the log alone. Curvature: the pure second derivatives came from two chained central differences, which evaluates them at a lag of 2h; measured on sine surfaces, that preserved 40 percent of the true amplitude for forms four cells across, against 81 percent for the three-point stencil now used -- and no quadratic test surface could ever detect it, because the second derivative of a degree-two polynomial is exact at any lag. Large DEMs: deriving terrain peaks at about 134 bytes per cell, and nothing capped it; grids above 40 million cells now raise a clear error telling the user to clip, and above 8 million a warning. Cancelling: the route search, the waypoint-order optimisation and the depression filling now report progress and honour Cancel, instead of holding QGIS for minutes with a dead button. Waypoint order also stops computing the roughly one fifth of pairs the dynamic program never reads. Processing toolbox: the cost model now defaults to walking time, as the dialog already did, instead of the inverse model the documentation itself describes as too flat. Stream crossing extent now accounts for diagonal steps. The regression test that protects the rule 'drainage is a cost, never a wall' used to reimplement that rule inside the test and check its own copy; the rule moved to combine_constraints() and the test calls it.
    1.1.2 - QGIS 4: the plugin repository's Qt6 checker flagged two unscoped enums (QgsColorRampShader.Interpolated) in the raster styling; now resolved through the version-agnostic helper, and the static compatibility test covers QGIS enum classes as well as Qt ones. No functional change.
    1.1.1 - Release hygiene for the QGIS plugin repository: its Bandit scan blocked 1.1.0 for eight try/except/pass blocks; every swallowed exception now leaves a trace in the QGIS message log, unused imports were pruned, and the same Bandit and Flake8 scans run in CI so this cannot happen again. metadata.txt no longer contains percent signs, which the repository's parser rejects. No functional change.
    1.1.0 - Graded river crossings. Not every watercourse is impassable: many trails ford shallow streams or cross on stones. The route now pays a cost that grows with the size of the stream, using the contributing area of the channel as a proxy for its size (hydraulic geometry, Leopold and Maddock 1953; Faustini et al. 2009): headwater streams below 2 km2 cost 2x, streams 2-10 km2 cost 4x, small rivers up to the fordable ceiling cost 8x, and above the ceiling (new parameter 'Largest fordable drainage basin', default 50 km2) the river is a barrier. Zones still exclude the drainage strip. Every crossing the route makes is written to a new output layer of points with contributing area, class, cost factor and a field-check warning, and is loaded into the project with the other products. The DEM knows the size of the basin, not today's depth or current, so the safety of a crossing is never asserted -- it is listed to be verified.
    1.0.0 - First stable release. End-to-end battery on three regions with very different relief -- Serra da Mantiqueira (real 1-arcsecond DEM, the Marins-Marinzinho-Itaguare traverse against the hiker's own GPS track and summit waypoints), Ceara (real Copernicus GLO-90, field GPS tracks, park polygon) and a Himalayan slope regime -- plus polar latitude, Web Mercator and memory-layer cases: 14 of 14 pass, and the full window was driven as a user would in all six languages. The battery found one real problem, fixed here: with the window's defaults, watercourses derived from the DEM entered the route as an absolute wall, so any traverse that had to cross a valley failed with 'could not connect'. Watercourses are now always a cost for the route (crossable at 8x) while zones still exclude them in 'avoid' mode; the user's own constraint layer keeps the chosen mode. The no-path error now lists the likely causes. Docs: docs/BATERIA_TESTES.md, validation/bateria_regioes.py.
    0.14.0 - Three independent adversarial audits (mathematics, geography, languages) before publication, and the fixes they demanded. Mathematics: cells next to NoData were differentiated against the scene mean, which invented slopes of hundreds of percent along every reprojected DEM's edge -- derivatives now use one-sided differences at the data edge (slope, curvature, VRM, TWI); criteria that do not cover a cell (extra raster, wetness, roughness) no longer count as the worst score but are left out with per-cell weight renormalisation; the stream buffer is circular; curvatures are now correctly cited (Moore et al. 1991 / Mitasova and Hofierka 1993, not Zevenbergen-Thorne); degenerate rasters get a clear message. Geography: DEMs in a projected CRS that does not measure in metres (State Plane feet, Web Mercator) are reprojected to the automatic UTM instead of being read as metres; rotated or rectangular-pixel grids are resampled to a square north-up grid; constraint layers come through QGIS features, so GeoPackage layers opened from the browser and memory layers work; custom output CRSs (USER:nnnn, no authority) work through WKT; the cell of a point is found with floor, not round; a point outside the DEM says so; UTM zone is right for 0-360 longitudes and a warning covers latitudes above 84; KML explains it is always WGS84; rasters inside a GeoPackage are accepted. Languages: the Processing help text, the step-4 summary, the map-pick prompts, file-dialog titles and filters, the layer names loaded into the project and the not-registered error were still Portuguese or English in the other languages -- all keyed now; combo boxes resize with the language; switching language no longer resets the cost model; locale strings like zh.UTF-8 detect correctly; the Chinese entry says Simplified; translation fixes in es, fr, zh, ja from a fluent review. Nineteen regression tests added.
    0.13.1 - Interface pass. Qt silently ignores fractional pixel font sizes, and seventeen of the window's type rules used them, so most text was rendering at the QGIS default instead of the designed scale; every size is now a whole pixel and the hierarchy finally reads as drawn. Header is one row shorter (About and version join the step row). Included products no longer look disabled: accent icon, dashed frame and an "Included" pill. Four form labels and one group subtitle that were written once at build time now follow the language switch, and the Qt6 harness checks that nothing stays behind in another language.
    0.13.0 - Zero external dependencies. Every remaining use of geopandas and shapely (zone vectorisation, vector export, route and corridor construction) now goes through GDAL/OGR, which QGIS itself ships on every platform: install the plugin and it runs, with nothing to pip install. Verified end to end in a headless QGIS with geopandas, shapely, pandas, fiona and pyproj blocked from import, in GeoPackage, Shapefile and KML, on projected and geographic DEMs. Along the way, KML export switched to the LIBKML driver, because the classic KML driver silently dropped every attribute but the first two. A new test forbids any module-level import that QGIS does not guarantee, so this cannot regress.
    0.12.2 - Publication-readiness audit, and the start of its most serious finding: the plugin imported geopandas at module level, which QGIS does not ship on any platform, so it failed to load on a clean QGIS install before showing a single window. The constraint layer now goes through OGR, which QGIS does guarantee; the remaining call sites follow. Also removed a 902-line module no import ever reached.
    0.12.1 - The window is now actually built and exercised under Qt6 -- the toolkit QGIS 4 uses -- in all six languages, and that found a break the static check had missed: QPainter.Antialiasing is also a scoped enum in Qt6. The harness runs in continuous integration from now on, so QGIS 4 support stops being an assumption.
    0.12.0 - QGIS 4 compatibility, verified rather than assumed. The plugin already declared support for QGIS 4, but Qt6 removed unscoped enum access -- Qt.PointingHandCursor no longer exists -- and 33 such accesses would have raised as soon as a user clicked. All of them now go through the version-agnostic helpers the project already had, and a test resolves every enum name against a real PyQt6 install, which catches a mistyped group that Qt5 would silently tolerate. The footer now credits the author alongside the institutional logos, in all six languages.
    0.11.2 - Layout fixes found by measuring every label in all six languages: no text is clipped any more. The clipping was in the Latin languages, not the Asian ones -- "Destination" needed 98 px in a slot fixed at 64, and the active step tab reserved its width in the normal font weight while rendering in semibold, so it cut the step the user was actually on. A test now checks all six languages at the minimum window size, because this defect is silent.
    0.11.1 - Audit fixes. The transitability class labels are now translated as well: they are written into the output raster's legend, so a Japanese user was opening the map in QGIS and reading a Portuguese legend -- and the file kept it when passed on. Also removed an interface file the rewritten window no longer loads, an unused widget class, and a stray run log, all of which were shipping inside the plugin package.
    0.11.0 - The plugin now speaks six languages: Portuguese, English, Spanish, French, Chinese and Japanese. It opens in the language QGIS is running in and remembers a different choice. The strings moved out of the Python source into one JSON file per language, so a wrong term can be fixed by whoever speaks the language rather than by whoever maintains the code, and the Processing toolbox labels went through the same mechanism -- they previously stayed in Portuguese in every language, because the Qt .qm files they relied on were never generated. Spanish, French, Chinese and Japanese are draft translations and say so in the interface; corrections are welcome.
    0.10.0 - The window was restructured around a horizontal stepper instead of a sidebar, freeing the full width for content, and it now follows the QGIS theme in light or dark with a manual override. Choosing a DEM shows what was actually read from it -- format, cell size, CRS and pixel count -- so a wrong file is caught before the analysis runs rather than after. Outputs that are always produced now say so with the word "included" instead of a ticked-and-greyed checkbox that read as switched off, option cards answer Space and Enter, and the footer carries a contextual line saying what is missing or what is selected.
    0.9.2 - A visual design pass. The plugin now has its own icon language: eighteen glyphs stroked with QPainter at the exact size and colour requested, so they stay sharp at any screen density and recolour with the state of the control, with no image files and no new dependency. Each output is presented as a selectable card with icon, title and one-line description instead of a checkbox in a list, so the screen can be scanned rather than read; the longer caveats moved into tooltips. Type scale, spacing and states were rebuilt around the deep forest green of the plugin's own logo.
    0.9.1 - Visual redesign of the assistant, around a sidebar in the deep forest green of the plugin's own logo, with the project and institutional logos restored to permanent view rather than hidden in an About box. Help texts no longer illustrate features with one country's place names: a tool published for worldwide use has to explain intermediate destinations as "one summit, then another", not by naming local peaks. Tests now fail the build if a regional example or a missing credit creeps back in.
    0.9.0 - The interface was rewritten as a four-step assistant. It had fallen behind the engine: sixteen parameters added since 0.6 -- deriving slope and curvature from the DEM, drainage, Tobler walking time, transitability breaks, intermediate destinations -- could not be reached from the window at all, and the transitability map was never loaded into the project because the dialog looked for an output name that never existed. Now only the DEM is required, every control carries a sentence explaining what it changes, options appear only when asked for, and the run happens in the background so QGIS stays usable. A contract test now fails the build whenever the window and the algorithm drift apart again.
    0.8.0 - Routes can now visit several destinations in order -- climb one summit, then the next, then descend -- instead of only a start and an end point, with optional exact optimisation of the visiting order. This closed the one case where the plugin lost to a straight line: declaring the three summits of the Marins-Itaguare traverse moved agreement with the real GPS track from 2.7 percent to 73.0 percent and median deviation from 1860 m to 114 m. Route constants were also recalibrated against route geometry rather than walking speed, which is what they actually govern: Tobler's published decay of 3.5 is confirmed, the terrain term is worth 12 points of agreement, and leave-one-out cross-validation showed the apparently better values do not generalise, so the defaults stand -- now on evidence.
    0.7.0 - First empirical validation against real field GPS: 110 h and 224 km of surveyed tracks in two biomes. Tobler's walking speed is optimistic by a factor of 1.7-3.1 for field survey work, but the route is provably unaffected -- only the estimated duration changes -- while the decay, which does move the route, could not be separated from its published value and is kept. Transitability labels no longer claim a walker cannot pass: real teams walked 115.8 percent slope. The class map now records the cell size and warns when it is coarse, because the same terrain reads as 48 percent or 76 percent walkable depending on resolution. Full record in docs/VALIDACAO.md.
    0.6.3 - Any raster the user has can now enter the model as an extra weighted criterion -- stoniness, vegetation cover, a cost surface -- aligned to the DEM grid and normalised by scene percentiles. Weighted zero by default.
    0.6.2 - New output: a transitability class map with absolute, field-meaningful classes and a colour palette written into the GeoTIFF, so it opens legible in QGIS with its legend. Slope sets the class; high ruggedness or high wetness push a cell one class worse; constraints marked as avoided become class 5.
    0.6.1 - Route cost can be estimated walking time using Tobler's anisotropic hiking function, so uphill and downhill no longer cost the same and the accumulated cost is in hours. Two optional criteria added, both derived from the DEM and both weighted zero by default: topographic wetness index and terrain ruggedness index. Fixes the reprojection fill entering the analysis as sea-level terrain.
    0.6.0 - Only the DEM is required: slope and both curvatures are derived from it by default, which removes the unit, sign-convention, grid-alignment and coverage problems in one step. Slope unit and DEM vertical unit are now explicit parameters. Watercourses can be extracted from the DEM and used as a constraint, and any vector layer can be used the same way. Route cost model is selectable. Potential zones now use the multicriteria model by default. Temporary directories are cleaned up.
    0.5.1 - QGIS 4 / Qt6 compatibility while preserving QGIS 3.22+ support; responsive interface for small screens and high-DPI scaling; theme-aware styling; experimental flag removed; interface text encoding corrected.
    0.5.0 - English documentation promoted to primary; Portuguese reference copies kept in docs/pt_BR; PT-BR | ENG language switch; legacy helper code removed; suitability, relative-risk, route and corridor workflow preserved.
    0.4.0 - Initial public beta.
