Changes in version 1.3.0 Surfaces digitised twice are detected A surface traced a second time counted twice, so a buffer that should read near 100% classified could report 191%. Polygons with identical geometry are now collapsed to one on read. Genuine overlap -- a tree crown over a roof -- is different geometry and is untouched. Which copy survives depends on what it carries: if only one has a class code that one is kept; if the codes conflict the first is kept and the conflicting pair is named, because silently choosing between two interpretations would hide a decision the interpreter should make. A new coverage_flag column marks any site still above 120% after that, so duplication the geometric test cannot see is reported rather than passed on quietly. Coverage above 100% remains legitimate and is not flagged. The aerial palette has been rebuilt Several classes were too close to tell apart, most visibly main thoroughfare against formal parking. Every pair is now either at least 14 CIELAB units apart or separated by a texture; measured across all 378 pairs, none is ambiguous. Where two classes genuinely look alike from above, the texture carries meaning: a grid for parking bays, vertical strokes for the deep shadow of an alley, horizontal strokes for the linear flow of a channel, a fine diagonal for a finished slab. Eight of the 28 classes now carry a texture, up from four. Legend Class labels were cut at 32 characters, losing the end of names such as "Fibre-cement or asbestos sheeting". The column is wider and labels now run to 40 characters, which covers the longest name in the schema. Wording "unsealed tank (oviposition risk)" is now "apparently unsealed tank", and "sealed tank" is "apparently sealed tank". What aerial imagery supports is an appearance, not a determination: a lid is not always visible from above, and the figures should not claim more than the method can deliver. Changes in version 1.2.1 Four failures reported from a 44-file field run, all fixed. Two of them could end a long run with nothing usable on disk. - Invalid geometry no longer aborts a site. Self-intersecting polygons -- common in hand digitising -- made GEOS raise TopologyException and the whole file was abandoned. Geometry is now repaired on read and again after reprojection, and an overlay that still fails is retried on repaired inputs, then at reduced precision, then polygon by polygon. Only genuinely irreparable polygons are skipped, and they are reported. - The workbook is always written. A site digitised in two different files produced duplicate rows, pivot_wider() returned list-columns, and writexl refused the lot -- discarding every table at the end of the run. Such sites are now kept separately as ID__file, matching how their maps are already named. As a backstop the writer flattens anything non-atomic, and if the workbook still cannot be written the tables are saved as CSV. - A site digitised twice in one file is recognised as one site rather than two. It previously doubled that site's reported coverage and produced a legend value of length two, which stopped the map from being drawn. - Legend cells are always scalar, so no future data condition can fail a map that way. Changes in version 1.2.0 The nomenclature now matches the published Table 1 mare_categories previously held 29 classes in 12 groups. The article and protocol define 28 classes in nine thematic groups, with class 29 (PVC roofing) removed once the nomenclature stabilised and class 24 revised. The dataset is now identical to Table 1: same identifiers, same English labels, same grouping. Identifiers stayed contiguous, so no surviving class was renumbered. This changes output column names. Columns are built as id_category_description, and category now carries the nine thematic groups (roofing, vegetation, circulation_parking, ...) rather than the earlier twelve Portuguese labels. Analyses that referred to the old column names must be updated. Areas, weights and counts are unchanged. Any data still coded with class 29 will be reported as an unrecognised code rather than silently dropped, which is the intended behaviour. Changes in version 1.1.0 GeoPackage support - buffer_composition() and batch_composition() now read GeoPackage as well as KML. The format is detected from the file's contents rather than its extension, and a folder may mix the two. A file may contain one site or several in either format; nothing needs to be configured. - A GeoPackage is expected to carry focal_points and landcover layers, and optionally buffers, water_containers, poi_lines and class_dictionary. Anything else falls back to the flat reader used for KML. - The same site read from KML and from GeoPackage returns identical areas, weights, container counts and reference distances; this is enforced by a test. Fixes - A buffer polygon digitised under the site's own name (NH_6 rather than NH_6_Buffer) was treated as an unclassified land-cover polygon and dropped with a warning. It is now recognised as reference geometry, so the buffer cross-check works on these files. - bufferscape::class_dictionary() failed with "object 'mare_categories' not found" unless the package had been attached with library(). Lazy-loaded data is not on the namespace search path; the lookup is now explicit. - The per-radius sheets written by batch_composition() were missing the _d_nearest_m proximity columns. Renamed arguments - buffer_composition(kml = ) is now path = , and batch_composition(kml_dir = ) is now dir = , because neither is KML-specific any more. The old names still work and warn. Changes in version 1.0.3 (2026-08-05) - Fixed a bug in batch_composition(): requesting a basemap with make_maps = TRUE failed with "could not find function basemap_ready". The helper that checks whether the imagery packages are installed had been lost in a refactor while its call site remained. - Declared the non-standard-evaluation column names used by \pkg{dplyr} and \pkg{ggplot2}, so R CMD check no longer reports undefined globals. Changes in version 1.0.2 - Progress output from batch_composition() now goes through message() rather than cat(), so a run can be silenced with suppressMessages(). CRAN policy asks that packages not write to the console in a way the user cannot suppress. - The archive DOI is recorded in README.md, CITATION.cff and inst/CITATION, so citation("bufferscape") returns it. Changes in version 1.0.1 - Version strings in DESCRIPTION, CITATION.cff and inst/CITATION now match the release tag; 1.0.0 was tagged while the package still declared 0.1.0. - Added CITATION.cff, so the repository exposes citation metadata to GitHub and to archives such as Zenodo. Changes in version 1.0.0 First release. Core - buffer_composition() computes per-class surface area within buffers around point locations, directly from vector polygons. It returns the exact area, a distance-decay weighted area in which the kernel is integrated over polygon geometry (area_w), the centroid-weighted equivalent for comparison (area_w_centroid), and the distance to the nearest patch of each class (d_nearest_m). - Overlapping polygons are supported; class areas are not constrained to sum to the buffer area. - decay_kernel() provides bounded exponential and Gaussian kernels. - batch_composition() processes a folder, writing a workbook and figures. A malformed file is logged and skipped rather than stopping the run. - A KML may contain several sampling points; polygons and point features are assigned to each site geometrically, against that site's own buffer. Classification - The dictionary is user-supplied. class_dictionary() accepts a data.frame or an .xlsx/.csv path; validate_dictionary() checks one before a long run. The 29-class Maré schema ships as mare_categories. - parse_class_codes() keeps hyphenated codes such as "8-6", which naive coercion would silently drop. Methods diagnostics - centroid_bias() and summarise_centroid_bias() quantify, polygon by polygon, how far centroid weighting departs from the exact integral, with shape descriptors and a centroid_inside flag for concave features whose centroid falls outside the polygon. Figures - map_composition() draws the land-cover map; map_basemap() and map_combined() add satellite imagery. - plot_site_composition() and plot_group_points() give per-site composition bars and per-group point-feature bars. - class_palette() provides four schemes: "aerial" (appearance-matched), "colorblind" (one colour-vision-safe hue per group, members separated by lightness and texture), "viridis" and "greyscale". resolve_palette() also accepts a palette table or a named colour vector. Export - write_composition_report() writes the workbook, with control over radii, which surface metrics to include, which sheets, label style and rounding.