Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .Rprofile

This file was deleted.

3 changes: 3 additions & 0 deletions CRAN-SUBMISSION
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Version: 0.4.4
Date: 2025-07-23 12:38:35 UTC
SHA: 11f3693d2f3ce60c26146e771d0dd17e7385d318
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Description: Parse messy geographic coordinates from various character formats
minutes, or seconds; add and subtract degrees, minutes,
and seconds. C++ code herein originally inspired from code
written by Jeffrey D. Bogan, but then completely re-written.
Version: 0.4.3
Version: 0.4.4
Authors@R: c(
person("Scott", "Chamberlain", role = "aut",
email = "sckott@protonmail.com",
Expand Down
8 changes: 8 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
parzer 0.4.4
============

### BUG FIX

* Fixed a bug in longitude conversion where "74E5423" would be converted to NA while "74W5423" was correctly converted.
* Improved C++ management of NA values.

parzer 0.4.3
============

Expand Down
24 changes: 4 additions & 20 deletions R/dms-fxns.R
Original file line number Diff line number Diff line change
Expand Up @@ -105,33 +105,17 @@ pz_s <- function(x) {
`+.pz` <- function(e1, e2) {
e1u <- unclass_strip_atts(e1)
e2u <- unclass_strip_atts(e2)
e1 <- switch(attr(e1, "type"),
deg = e1u,
min = e1u / 60,
sec = e1u / 3600
)
e2 <- switch(attr(e2, "type"),
deg = e2u,
min = e2u / 60,
sec = e2u / 3600
)
e1 <- switch(attr(e1, "type"), deg = e1u, min = e1u / 60, sec = e1u / 3600)
e2 <- switch(attr(e2, "type"), deg = e2u, min = e2u / 60, sec = e2u / 3600)
structure(e1 + e2, class = "pz", type = "deg")
}
#' @export
#' @rdname dms
`-.pz` <- function(e1, e2) {
e1u <- unclass_strip_atts(e1)
e2u <- unclass_strip_atts(e2)
e1 <- switch(attr(e1, "type"),
deg = e1u,
min = e1u / 60,
sec = e1u / 3600
)
e2 <- switch(attr(e2, "type"),
deg = e2u,
min = e2u / 60,
sec = e2u / 3600
)
e1 <- switch(attr(e1, "type"), deg = e1u, min = e1u / 60, sec = e1u / 3600)
e2 <- switch(attr(e2, "type"), deg = e2u, min = e2u / 60, sec = e2u / 3600)
structure(e1 - e2, class = "pz", type = "deg")
}
#' @export
Expand Down
2 changes: 1 addition & 1 deletion R/parzer-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ NULL
#' `r lifecycle::badge("stable")`
#'
#' parse geographic coordinates
"_PACKAGE"
"_PACKAGE"
4 changes: 3 additions & 1 deletion R/zzz.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
assert <- function(x, y) {
if (!is.null(x)) {
if (!inherits(x, y)) {
stop(deparse(substitute(x)), " must be of class ",
stop(
deparse(substitute(x)),
" must be of class ",
paste0(y, collapse = ", "),
call. = FALSE
)
Expand Down
12 changes: 3 additions & 9 deletions codemeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,17 @@
"identifier": "parzer",
"description": "Parse messy geographic coordinates from various character formats to decimal degree numeric values. Parse coordinates into their parts (degree, minutes, seconds); calculate hemisphere from coordinates; pull out individually degrees, minutes, or seconds; add and subtract degrees, minutes, and seconds. C++ code herein originally inspired from code written by Jeffrey D. Bogan, but then completely re-written.",
"name": "parzer: Parse Messy Geographic Coordinates",
"relatedLink": ["https://docs.ropensci.org/parzer/", "https://CRAN.R-project.org/package=parzer"],
"relatedLink": "https://docs.ropensci.org/parzer/",
"codeRepository": "https://github.com/ropensci/parzer",
"issueTracker": "https://github.com/ropensci/parzer/issues",
"license": "https://spdx.org/licenses/MIT",
"version": "0.4.3",
"version": "0.4.4",
"programmingLanguage": {
"@type": "ComputerLanguage",
"name": "R",
"url": "https://r-project.org"
},
"runtimePlatform": "R version 4.5.0 (2025-04-11)",
"provider": {
"@id": "https://cran.r-project.org",
"@type": "Organization",
"name": "Comprehensive R Archive Network (CRAN)",
"url": "https://cran.r-project.org"
},
"author": [
{
"@type": "Person",
Expand Down Expand Up @@ -198,7 +192,7 @@
"applicationCategory": "Geospatial",
"isPartOf": "https://ropensci.org",
"keywords": ["geospatial", "data", "latitude", "longitude", "parser", "coordinates", "r", "rstats", "geo", "r-package"],
"fileSize": "4749.036KB",
"fileSize": "6120.123KB",
"releaseNotes": "https://github.com/ropensci/parzer/blob/master/NEWS.md",
"readme": "https://github.com/ropensci/parzer/blob/main/README.md",
"contIntegration": ["https://github.com/ropensci/parzer/actions/", "https://app.codecov.io/github/ropensci/parzer?branch=main"],
Expand Down
12 changes: 4 additions & 8 deletions cran-comments.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
## R CMD check results

0 errors | 0 warnings | 1 note

* NOTE: unable to verify current time
0 errors | 0 warnings | 0 note

* This is a new release.
* `revdepcheck` passed
* Local and GHA and win_devel R CMD check passed

## Changes not listed in NEWS

* Removed the README file from the package directory as requested by Professor Ripley.
* Local and GHA and win_devel and r-project macOS buider R CMD check passed

Dear CRAN reviewer,

The package was silently archived because of a C++ undefined cast. Hopefully this has been fixed.

Thank you for your time reviewing this release.

Dr Alban Sagouis
11 changes: 6 additions & 5 deletions revdep/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@
|collate |en_US.UTF-8 |
|ctype |en_US.UTF-8 |
|tz |Europe/Berlin |
|date |2025-05-19 |
|date |2025-07-22 |
|pandoc |3.6.4 @ /opt/homebrew/bin/pandoc |
|quarto |1.6.40 @ /Applications/Positron.app/Contents/Resources/app/quarto/bin/quarto |
|quarto |1.7.31 @ /Applications/Positron.app/Contents/Resources/app/quarto/bin/quarto |

# Dependencies

|package |old |new |Δ |
|:-------|:-----|:-----|:--|
|parzer |0.4.1 |0.4.2 |* |
|package |old |new |Δ |
|:-------|:---|:-----|:--|
|parzer |NA |0.4.4 |* |
|Rcpp |NA |1.1.0 |* |

# Revdeps

2 changes: 1 addition & 1 deletion src/RcppExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ BEGIN_RCPP
END_RCPP
}
// split_decimal_degree
std::vector<double> split_decimal_degree(const double& x, const std::string& fmt);
Rcpp::List split_decimal_degree(const double& x, const std::string& fmt);
RcppExport SEXP _parzer_split_decimal_degree(SEXP xSEXP, SEXP fmtSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Expand Down
6 changes: 3 additions & 3 deletions src/latlong.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,11 @@ bool has_non_direction_letters(std::string& s, const std::string& reggex) {
return z;
}

// useful to NA on "-45.23232e24"
bool has_e_with_trailing_numbers(const std::string& s) {
bool res = false;
// s = str_tolower(s);
std::regex reg("[0-9]+e[0-9]+");
std::regex reg("[0-9]+e[0-9]+$");
std::smatch match;
if (std::regex_search(s, match, reg)) {
res = true;
Expand Down Expand Up @@ -243,7 +244,7 @@ double convert_lon(std::string& str) {
if (
str.size() == 0 ||
!any_digits(str) ||
has_non_direction_letters(str, "abcfghijklmnopqrstuvxyz") ||
has_non_direction_letters(str, "abcfghijklmnopqrstuvxyz") ||
has_e_with_trailing_numbers(str)
) {
ret = NA_REAL;
Expand Down Expand Up @@ -294,4 +295,3 @@ double convert_lon(std::string& str) {
}
return ret;
}

19 changes: 11 additions & 8 deletions src/pz_parse_parts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
#include "latlong.h"

// [[Rcpp::export]]
std::vector<double> split_decimal_degree(const double& x, const std::string& fmt = "dms") {
Rcpp::List split_decimal_degree(const double& x, const std::string& fmt = "dms") {
//std::vector<double> split_decimal_degree(const double& x, const std::string& fmt = "dms") {
const double sixty = 60;
const double thirtysixh = 3600;
double dir_val = 1.0;

if ( R_IsNA(x)) {
std::vector<double>{0, 0, 0}; // this has to be updated
// return Rcpp::List::create(NA_REAL, NA_REAL, NA_REAL);
if (std::isnan(x)) {
//std::vector<double>{0, 0, 0}; // this has to be updated
return Rcpp::List::create(NA_REAL, NA_REAL, NA_REAL);
}
// auto x_str = Rcpp::toString(x); // Rcpp should be replaced here
// if (is_negative(x)) { // does not use Rcpp but uses regex
Expand All @@ -23,7 +24,8 @@ std::vector<double> split_decimal_degree(const double& x, const std::string& fmt
double s = ((x_abs - d) - (m/sixty)) * thirtysixh;
d = d * dir_val;

return std::vector<double>{d, m, s};
return Rcpp::List::create(d, m, s);
// return std::vector<double>{d, m, s};
}

// [[Rcpp::export]]
Expand All @@ -35,7 +37,8 @@ Rcpp::DataFrame pz_parse_parts_lat(std::vector<std::string>& x) {

for (int i=0; i < n; ++i) {
double out = convert_lat(x[i]); // passed as a reference.
std::vector<double> parts = split_decimal_degree(out); // passed as a const reference.
//std::vector<double> = split_decimal_degree(out); // passed as a const reference.
Rcpp::List parts = split_decimal_degree(out); // passed as a const reference.
deg[i] = parts[0];
min[i] = parts[1];
sec[i] = parts[2];
Expand All @@ -55,7 +58,8 @@ Rcpp::DataFrame pz_parse_parts_lon(std::vector<std::string>& x) {

for (int i=0; i < n; ++i) {
double out = convert_lon(x[i]); // passed as a reference.
std::vector<double> parts = split_decimal_degree(out); // passed as a const reference.
//std::vector<double> parts = split_decimal_degree(out); // passed as a const reference.
Rcpp::List parts = split_decimal_degree(out); // passed as a const reference.
deg[i] = parts[0];
min[i] = parts[1];
sec[i] = parts[2];
Expand All @@ -65,4 +69,3 @@ Rcpp::DataFrame pz_parse_parts_lon(std::vector<std::string>& x) {
Rcpp::_["sec"] = sec,
Rcpp::_["stringsAsFactors"] = false);
}

6 changes: 6 additions & 0 deletions tests/testthat/setup.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
base::options(digits = 7)
suppressWarnings({
suppressMessages({
if (isTRUE(require(paint))) paint::unmask_print()
})
})
3 changes: 2 additions & 1 deletion tests/testthat/test-dms.R
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ invalid_formats <- c(

test_that("dms fxns fail as expected", {
out <- data.frame(
input = invalid_formats, res = NA_real_,
input = invalid_formats,
res = NA_real_,
stringsAsFactors = FALSE
)
for (i in seq_along(invalid_formats)) {
Expand Down
17 changes: 13 additions & 4 deletions tests/testthat/test-parse_hemisphere.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@ test_that("parse_hemisphere works", {
# bad values
## one
expect_equal(
suppressWarnings(parse_hemisphere("120", "-240.4183318")), "E")
suppressWarnings(parse_hemisphere("120", "-240.4183318")),
"E"
)
expect_equal(
suppressWarnings(parse_hemisphere("420", "-40.4183318")), "S")
suppressWarnings(parse_hemisphere("420", "-40.4183318")),
"S"
)
## both
expect_equal(
suppressWarnings(parse_hemisphere("-200", "-240.4183318")), "")
suppressWarnings(parse_hemisphere("-200", "-240.4183318")),
""
)
})

test_that("parse_hemisphere - fails well", {
Expand All @@ -32,5 +38,8 @@ test_that("parse_hemisphere - fails well", {

expect_warning(parse_hemisphere("45", "190"), "not within -90")
expect_warning(parse_hemisphere("45", "190"), "check that you did not invert")
expect_warning(parse_hemisphere("190", "45"), "longitude value within 180/360 range, got: 190")
expect_warning(
parse_hemisphere("190", "45"),
"longitude value within 180/360 range, got: 190"
)
})
24 changes: 20 additions & 4 deletions tests/testthat/test-parse_lat.R
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ test_lats <- c(

test_that("parse_lat works: run through test_lats", {
out <- data.frame(
input = test_lats, res = NA_real_,
input = test_lats,
res = NA_real_,
stringsAsFactors = FALSE
)
for (i in seq_along(test_lats)) {
Expand Down Expand Up @@ -77,13 +78,28 @@ invalid_formats <- c(
# res column should all give NaN
test_that("parse_lat works: invalid formats fail as expected", {
out <- data.frame(
input = invalid_formats, res = NA_real_,
input = invalid_formats,
res = NA_real_,
stringsAsFactors = FALSE
)
for (i in seq_along(invalid_formats)) {
out[i, "res"] <- suppressWarnings({parse_lat(invalid_formats[i])})
expect_warning({aa <- parse_lat(invalid_formats[i])})
out[i, "res"] <- suppressWarnings({
parse_lat(invalid_formats[i])
})
expect_warning({
aa <- parse_lat(invalid_formats[i])
})
expect_type(aa, "double")
expect_equal(aa, NaN)
}
})


test_that("parse_lat correctly processes NA values", {
expect_equal(
suppressWarnings(
parse_lat(c("W60.1", NA, NA_character_, "12' 30'"))
),
c(NA_real_, NA_real_, NA_real_, 12.5)
)
})
22 changes: 22 additions & 0 deletions tests/testthat/test-parse_lat_lon.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,25 @@ test_that("parse_lon_lat - fails well", {
expect_warning(parse_lon_lat("45", "190"), "not within -90")
expect_warning(parse_lon_lat("45", "190"), "check that you did not invert")
})


test_that("parse_lon_lat correctly processes NA values", {
expect_equal(
suppressWarnings(
parse_lon_lat("S60.1", NA_character_)
),
data.frame(lon = NA_real_, lat = NA_real_)
)
expect_equal(
suppressWarnings(
parse_lon_lat("12' 30'", NA_character_)
),
data.frame(lon = 12.5, lat = NA_real_)
)
expect_equal(
suppressWarnings(
parse_lon_lat(NA_character_, "12' 30'")
),
data.frame(lon = NA_real_, lat = 12.5)
)
})
9 changes: 9 additions & 0 deletions tests/testthat/test-parse_llstr.R
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,12 @@ test_that("parse_llstr - fails well", {
expect_warning(parse_llstr("190, 45"), "not within -90")
expect_warning(parse_llstr("190, 45"), "check that you did not invert")
})

test_that("parse_llstr correctly processes NA values", {
expect_equal(
suppressWarnings(
parse_llstr(c(NA_character_, "12' 30', 12' 30'"))
),
data.frame(lat = c(NA_real_, 12.5), lon = c(NA_real_, 12.5))
)
})
Loading
Loading