From 4d7f0f0f2f4cb9d392197d2b2347c92d3a380570 Mon Sep 17 00:00:00 2001 From: merkato Date: Thu, 17 Nov 2022 18:39:32 +0100 Subject: [PATCH 01/35] matrix --- R/main.R | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/R/main.R b/R/main.R index a3d2d25..a078fa3 100644 --- a/R/main.R +++ b/R/main.R @@ -191,14 +191,16 @@ litchi.plan = function(roi, output, hasCurve = !anyNA(curve) if (hasCurve) { if (curve$before) { - wptsMatrix[mat_pos,] = c(curve[,1:2], TRUE, FALSE) + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) + # wptsMatrix[mat_pos,] = c(curve[,1:2], TRUE, FALSE) mat_pos = mat_pos + 1 wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) mat_pos = mat_pos + 1 } else { wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) mat_pos = mat_pos + 1 - wptsMatrix[mat_pos,] = cbind(curve[,1:2], TRUE, FALSE) + # wptsMatrix[mat_pos,] = cbind(curve[,1:2], TRUE, FALSE) + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) mat_pos = mat_pos + 1 } } else { From 75b48805aaf27b258ea12e6beaf48102521f6113 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Thu, 17 Nov 2022 23:02:07 +0100 Subject: [PATCH 02/35] get rid of readOGR in tests --- flightplanning.Rproj | 44 +++++++++++++++++++++---------------------- tests/testthat/test.R | 35 +++++++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/flightplanning.Rproj b/flightplanning.Rproj index 5979669..be6e327 100644 --- a/flightplanning.Rproj +++ b/flightplanning.Rproj @@ -1,22 +1,22 @@ -Version: 1.0 - -RestoreWorkspace: Default -SaveWorkspace: Default -AlwaysSaveHistory: Default - -EnableCodeIndexing: Yes -UseSpacesForTab: Yes -NumSpacesForTab: 2 -Encoding: UTF-8 - -RnwWeave: Sweave -LaTeX: pdfLaTeX - -AutoAppendNewline: Yes -StripTrailingWhitespace: Yes - -BuildType: Package -PackageUseDevtools: Yes -PackageInstallArgs: --no-multiarch --with-keep.source -PackageCheckArgs: --as-cran -PackageRoxygenize: rd,collate,namespace +Version: 1.0 + +RestoreWorkspace: Default +SaveWorkspace: Default +AlwaysSaveHistory: Default + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: Sweave +LaTeX: pdfLaTeX + +AutoAppendNewline: Yes +StripTrailingWhitespace: Yes + +BuildType: Package +PackageUseDevtools: Yes +PackageInstallArgs: --no-multiarch --with-keep.source +PackageCheckArgs: --as-cran +PackageRoxygenize: rd,collate,namespace diff --git a/tests/testthat/test.R b/tests/testthat/test.R index 69e9cd9..3f297d1 100644 --- a/tests/testthat/test.R +++ b/tests/testthat/test.R @@ -146,7 +146,10 @@ test_that("Shutter speed calculation is correct", { test_that("Litchi plan outputs the csv file", { - exampleBoundary = readOGR(system.file("extdata", "exampleBoundary.shp", package="flightplanning"), "exampleBoundary") + exampleBoundary = sf::st_read( + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> + sf::as_Spatial() + outPath = tempfile(fileext=".csv") params = flight.parameters( @@ -166,7 +169,9 @@ test_that("Litchi plan outputs the csv file", { test_that("Different starting points are working", { - exampleBoundary = readOGR(system.file("extdata", "exampleBoundary.shp", package="flightplanning"), "exampleBoundary") + exampleBoundary = sf::st_read( + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> + sf::as_Spatial() outPath = tempfile(fileext=".csv") params = flight.parameters( @@ -196,7 +201,9 @@ test_that("Different starting points are working", { test_that("Different flight line angles are working", { - exampleBoundary = readOGR(system.file("extdata", "exampleBoundary.shp", package="flightplanning"), "exampleBoundary") + exampleBoundary = sf::st_read( + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> + sf::as_Spatial() outPath = tempfile(fileext=".csv") params = flight.parameters( @@ -233,15 +240,21 @@ test_that("Did not provide legal ROI", { test_that("ROI is not in a metric projection", { outPath = tempfile(fileext=".csv") - exampleBoundary = readOGR(system.file("extdata", "exampleBoundary.shp", package="flightplanning"), "exampleBoundary") - roi = exampleBoundary - roi = sp::spTransform(roi, "+init=epsg:4326") + exampleBoundary = sf::st_read( + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> + sf::as_Spatial() + roi = exampleBoundary |> + sf::st_as_sf() |> + sf::st_transform(crs = "EPSG:4326") |> + sf::as_Spatial() expect_error( litchi.plan(roi, outPath, NA) ) }) test_that("Did not provide Flight Parameters", { - exampleBoundary = readOGR(system.file("extdata", "exampleBoundary.shp", package="flightplanning"), "exampleBoundary") + exampleBoundary = sf::st_read( + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> + sf::as_Spatial() outPath = tempfile(fileext=".csv") expect_error( litchi.plan(exampleBoundary, outPath, NA) ) }) @@ -249,7 +262,9 @@ test_that("Did not provide Flight Parameters", { test_that("Break waypoints too far", { outPath = tempfile(fileext=".csv") - exampleBoundary = readOGR(system.file("extdata", "exampleBoundary.shp", package="flightplanning"), "exampleBoundary") + exampleBoundary = sf::st_read( + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> + sf::as_Spatial() params = flight.parameters( gsd = 4, side.overlap = 0, @@ -267,7 +282,9 @@ test_that("Break waypoints too far", { test_that("Break flight if exceeds max flight time", { outPath = tempfile(fileext=".csv") - exampleBoundary = readOGR(system.file("extdata", "exampleBoundary.shp", package="flightplanning"), "exampleBoundary") + exampleBoundary = sf::st_read( + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> + sf::as_Spatial() params = flight.parameters( gsd = 4, side.overlap = 0, From bc4e5e4259a28a4eac750dc0bb9bd3d0ce6955b2 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Thu, 17 Nov 2022 23:08:31 +0100 Subject: [PATCH 03/35] adopting to R >= 4.2.0, backward compatible with previous versions --- R/main.R | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/R/main.R b/R/main.R index a078fa3..b9fe5dd 100644 --- a/R/main.R +++ b/R/main.R @@ -188,18 +188,24 @@ litchi.plan = function(roi, output, mat_pos = 1 for (i in seq_len(nrow(waypoints))) { curve = as.vector(adjustedCurves[as.character(i),]) + + # From R 4.2.0 onwards: + # "as.vector() gains a data.frame method which returns a simple + # named list, also clearing a long standing ‘FIXME’ to enable + # as.vector(, mode="list"). This breaks code relying + # on as.vector() to return the unchanged data frame." + # therefore changing curve[,1:2] to curve[1,2] and removing cbind + hasCurve = !anyNA(curve) if (hasCurve) { if (curve$before) { wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) - # wptsMatrix[mat_pos,] = c(curve[,1:2], TRUE, FALSE) mat_pos = mat_pos + 1 wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) mat_pos = mat_pos + 1 } else { wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) mat_pos = mat_pos + 1 - # wptsMatrix[mat_pos,] = cbind(curve[,1:2], TRUE, FALSE) wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) mat_pos = mat_pos + 1 } From 58c684b323a86f910a702372b883b8524f609930 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Thu, 17 Nov 2022 23:18:30 +0100 Subject: [PATCH 04/35] readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index f73e94a..535bb7c 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,11 @@ This package should be installed using the devtools. devtools::install_github("caiohamamura/flightplanning-R") ``` +## This fork + +* adopts the package to R >= 4.2.0 (backward compatible) - DONE +* try to replace {rgdal}, {rgeos} and {sp} with {sf} - WIP + ## Usage There are two main functions available: * `flight.parameters()`: this will calculate the flight parameters given desired settings for GSD/height, target overlap, flight speed and camera specifications. From ead3f0039f5f58d7185eec000bc7c370cbe08522 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Fri, 18 Nov 2022 23:27:29 +0100 Subject: [PATCH 05/35] replace minBbox with original function shotGroups::getMinBBox(vertices) --- DESCRIPTION | 6 +- NAMESPACE | 3 +- R/main.R | 10 ++- R/utils.R | 149 +++++++++++++++++++++--------------------- man/getMinBBox.Rd | 17 ----- tests/testthat.R | 1 + tests/testthat/test.R | 1 - uav.gpkg | Bin 0 -> 98304 bytes 8 files changed, 90 insertions(+), 97 deletions(-) delete mode 100644 man/getMinBBox.Rd create mode 100644 uav.gpkg diff --git a/DESCRIPTION b/DESCRIPTION index db01147..054dd9a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -15,12 +15,14 @@ Imports: methods, rgdal, rgeos, - sp + sp, + shotGroups, + sf Depends: R (>= 3.0) Suggests: testthat License: MIT + file LICENSE Encoding: UTF-8 LazyData: true -RoxygenNote: 7.1.0 +RoxygenNote: 7.2.1 URL: https://github.com/caiohamamura/flightplanning-R BugReports: https://github.com/caiohamamura/flightplanning-R/issues diff --git a/NAMESPACE b/NAMESPACE index 692ae0b..72eec4a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -7,7 +7,8 @@ import(rgeos) import(sp) importFrom(grDevices,chull) importFrom(graphics,text) +importFrom(methods,slot) +importFrom(shotGroups,getMinBBox) importFrom(utils,data) importFrom(utils,read.csv) importFrom(utils,write.csv) -importFrom("methods", "slot") diff --git a/R/main.R b/R/main.R index b9fe5dd..c8ad710 100644 --- a/R/main.R +++ b/R/main.R @@ -57,7 +57,10 @@ DIAG_35MM = sqrt(36^2 + 24^2) # Classical 35mm film diagonal #' @export #' @import sp rgeos rgdal #' @importFrom graphics text +#' @importFrom shotGroups getMinBBox +#' @importFrom methods slot #' @importFrom utils data read.csv write.csv +#' litchi.plan = function(roi, output, flight.params, gimbal.pitch.angle = -90, flight.lines.angle = -1, max.waypoints.distance = 2000, @@ -84,10 +87,11 @@ litchi.plan = function(roi, output, minBbox = getBBoxAngle(vertices, flight.lines.angle) } else { # if angle not specified use minimum possible bounding box - minBbox = getMinBBox(vertices) + minBbox = shotGroups::getMinBBox(vertices) +# minBbox = getMinBBox(vertices) } - width = minBbox$width - height = minBbox$height + width = minBbox$heigh + height = minBbox$width alpha = minBbox$angle rads = alpha*pi/180 centroid = apply(minBbox$pts, 2, mean) diff --git a/R/utils.R b/R/utils.R index ba17bb8..915de1b 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,76 +1,79 @@ -#' Rotating calipers algorithm -#' -#' @description -#' Calculates the minimum oriented bounding box using the -#' rotating calipers algorithm. -#' Credits go to Daniel Wollschlaeger -#' -#' @param xy A matrix of xy values from which to calculate the minimum oriented -#' bounding box. -#' -#' @importFrom grDevices chull -getMinBBox <- function(xy) { - stopifnot(is.matrix(xy), is.numeric(xy), nrow(xy) >= 2, ncol(xy) == 2) - - ## rotating calipers algorithm using the convex hull - H <- grDevices::chull(xy) ## hull indices, vertices ordered clockwise - n <- length(H) ## number of hull vertices - hull <- xy[H, ] ## hull vertices - - ## unit basis vectors for all subspaces spanned by the hull edges - hDir <- diff(rbind(hull, hull[1, ])) ## hull vertices are circular - hLens <- sqrt(rowSums(hDir^2)) ## length of basis vectors - huDir <- diag(1/hLens) %*% hDir ## scaled to unit length - - ## unit basis vectors for the orthogonal subspaces - ## rotation by 90 deg -> y' = x, x' = -y - ouDir <- cbind(-huDir[ , 2], huDir[ , 1]) - - ## project hull vertices on the subspaces spanned by the hull edges, and on - ## the subspaces spanned by their orthogonal complements - in subspace coords - projMat <- rbind(huDir, ouDir) %*% t(hull) - - ## range of projections and corresponding width/height of bounding rectangle - rangeH <- matrix(numeric(n*2), ncol=2) ## hull edge - rangeO <- matrix(numeric(n*2), ncol=2) ## orthogonal subspace - widths <- numeric(n) - heights <- numeric(n) - - for(i in seq(along=numeric(n))) { - rangeH[i, ] <- range(projMat[ i, ]) - - ## the orthogonal subspace is in the 2nd half of the matrix - rangeO[i, ] <- range(projMat[n+i, ]) - widths[i] <- abs(diff(rangeH[i, ])) - heights[i] <- abs(diff(rangeO[i, ])) - } - - ## extreme projections for min-area rect in subspace coordinates - ## hull edge leading to minimum-area - eMin <- which.min(widths*heights) - hProj <- rbind( rangeH[eMin, ], 0) - oProj <- rbind(0, rangeO[eMin, ]) - - ## move projections to rectangle corners - hPts <- sweep(hProj, 1, oProj[ , 1], "+") - oPts <- sweep(hProj, 1, oProj[ , 2], "+") - - ## corners in standard coordinates, rows = x,y, columns = corners - ## in combined (4x2)-matrix: reverse point order to be usable in polygon() - ## basis formed by hull edge and orthogonal subspace - basis <- cbind(huDir[eMin, ], ouDir[eMin, ]) - hCorn <- basis %*% hPts - oCorn <- basis %*% oPts - pts <- t(cbind(hCorn, oCorn[ , c(2, 1)])) - - ## angle of longer edge pointing up - dPts <- diff(pts) - e <- dPts[which.max(rowSums(dPts^2)), ] ## one of the longer edges - eUp <- e * sign(e[2]) ## rotate upwards 180 deg if necessary - deg <- atan2(eUp[2], eUp[1])*180 / pi ## angle in degrees - - return(list(pts=pts, width=heights[eMin], height=widths[eMin], angle=deg)) -} +# #' below function is almost copy of shotGroups::getMinBBox() +# #' therefore replaced, GS, 2022-11-18 +# +# #' Rotating calipers algorithm +# #' +# #' @description +# #' Calculates the minimum oriented bounding box using the +# #' rotating calipers algorithm. +# #' Credits go to Daniel Wollschlaeger +# #' +# #' @param xy A matrix of xy values from which to calculate the minimum oriented +# #' bounding box. +# #' +# #' @importFrom grDevices chull +# getMinBBox <- function(xy) { +# stopifnot(is.matrix(xy), is.numeric(xy), nrow(xy) >= 2, ncol(xy) == 2) +# +# ## rotating calipers algorithm using the convex hull +# H <- grDevices::chull(xy) ## hull indices, vertices ordered clockwise +# n <- length(H) ## number of hull vertices +# hull <- xy[H, ] ## hull vertices +# +# ## unit basis vectors for all subspaces spanned by the hull edges +# hDir <- diff(rbind(hull, hull[1, ])) ## hull vertices are circular +# hLens <- sqrt(rowSums(hDir^2)) ## length of basis vectors +# huDir <- diag(1/hLens) %*% hDir ## scaled to unit length +# +# ## unit basis vectors for the orthogonal subspaces +# ## rotation by 90 deg -> y' = x, x' = -y +# ouDir <- cbind(-huDir[ , 2], huDir[ , 1]) +# +# ## project hull vertices on the subspaces spanned by the hull edges, and on +# ## the subspaces spanned by their orthogonal complements - in subspace coords +# projMat <- rbind(huDir, ouDir) %*% t(hull) +# +# ## range of projections and corresponding width/height of bounding rectangle +# rangeH <- matrix(numeric(n*2), ncol=2) ## hull edge +# rangeO <- matrix(numeric(n*2), ncol=2) ## orthogonal subspace +# widths <- numeric(n) +# heights <- numeric(n) +# +# for(i in seq(along=numeric(n))) { +# rangeH[i, ] <- range(projMat[ i, ]) +# +# ## the orthogonal subspace is in the 2nd half of the matrix +# rangeO[i, ] <- range(projMat[n+i, ]) +# widths[i] <- abs(diff(rangeH[i, ])) +# heights[i] <- abs(diff(rangeO[i, ])) +# } +# +# ## extreme projections for min-area rect in subspace coordinates +# ## hull edge leading to minimum-area +# eMin <- which.min(widths*heights) +# hProj <- rbind( rangeH[eMin, ], 0) +# oProj <- rbind(0, rangeO[eMin, ]) +# +# ## move projections to rectangle corners +# hPts <- sweep(hProj, 1, oProj[ , 1], "+") +# oPts <- sweep(hProj, 1, oProj[ , 2], "+") +# +# ## corners in standard coordinates, rows = x,y, columns = corners +# ## in combined (4x2)-matrix: reverse point order to be usable in polygon() +# ## basis formed by hull edge and orthogonal subspace +# basis <- cbind(huDir[eMin, ], ouDir[eMin, ]) +# hCorn <- basis %*% hPts +# oCorn <- basis %*% oPts +# pts <- t(cbind(hCorn, oCorn[ , c(2, 1)])) +# +# ## angle of longer edge pointing up +# dPts <- diff(pts) +# e <- dPts[which.max(rowSums(dPts^2)), ] ## one of the longer edges +# eUp <- e * sign(e[2]) ## rotate upwards 180 deg if necessary +# deg <- atan2(eUp[2], eUp[1])*180 / pi ## angle in degrees +# +# return(list(pts=pts, width=heights[eMin], height=widths[eMin], angle=deg)) +# } #' Provided an angle, calculate the corresponding minimum bounding box #' diff --git a/man/getMinBBox.Rd b/man/getMinBBox.Rd deleted file mode 100644 index 46f3d66..0000000 --- a/man/getMinBBox.Rd +++ /dev/null @@ -1,17 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/utils.R -\name{getMinBBox} -\alias{getMinBBox} -\title{Rotating calipers algorithm} -\usage{ -getMinBBox(xy) -} -\arguments{ -\item{xy}{A matrix of xy values from which to calculate the minimum oriented -bounding box.} -} -\description{ -Calculates the minimum oriented bounding box using the -rotating claipers algorithm. -Credits go to Daniel Wollschlaeger -} diff --git a/tests/testthat.R b/tests/testthat.R index 12af68c..61ff3f1 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,6 +1,7 @@ ## load dependencies library(testthat) library(flightplanning) +library(sf) ## test package test_check('flightplanning') diff --git a/tests/testthat/test.R b/tests/testthat/test.R index 3f297d1..b8bf5ba 100644 --- a/tests/testthat/test.R +++ b/tests/testthat/test.R @@ -295,6 +295,5 @@ test_that("Break flight if exceeds max flight time", { litchi.plan(exampleBoundary, outPath, params, max.flight.time = 10) title("Break into multiple flights") - expect_equal(length(Sys.glob(paste0(tools::file_path_sans_ext(outPath), "*.csv"))), 3) }) diff --git a/uav.gpkg b/uav.gpkg new file mode 100644 index 0000000000000000000000000000000000000000..bd1a16881fec585cee583f54dae56e7d5f610a7d GIT binary patch literal 98304 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 Date: Sat, 19 Nov 2022 14:26:55 +0100 Subject: [PATCH 06/35] parallel = TRUE|FALSE --- R/main.R | 32 +++++++++++++++++++++++++------- README.md | 3 ++- man/litchi.plan.Rd | 5 ++++- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/R/main.R b/R/main.R index c8ad710..c758e2c 100644 --- a/R/main.R +++ b/R/main.R @@ -20,6 +20,7 @@ DIAG_35MM = sqrt(36^2 + 24^2) # Classical 35mm film diagonal #' @param max.flight.time maximum flight time. If mission is greater than the estimated #' time, it will be splitted into smaller missions. #' @param starting.point numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 +#' @param parallel logical (default TRUE). Change direction of the fly lines over polygon from parallel to perpendicular #' #' @note this function will feed the csv flight plan with the `gimbal.pitch.angle` #' and the `photo time interval` for each waypoint, but those are not supported @@ -61,10 +62,15 @@ DIAG_35MM = sqrt(36^2 + 24^2) # Classical 35mm film diagonal #' @importFrom methods slot #' @importFrom utils data read.csv write.csv #' -litchi.plan = function(roi, output, - flight.params, gimbal.pitch.angle = -90, - flight.lines.angle = -1, max.waypoints.distance = 2000, - max.flight.time = 15, starting.point = 1) { +litchi.plan = function(roi, + output, + flight.params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + parallel = TRUE) { # Check parameters if (class(roi)[1] != "SpatialPolygonsDataFrame") stop("ROI is not a valid polygon layer") @@ -72,6 +78,10 @@ litchi.plan = function(roi, output, stop("ROI is not in a metric projection") if (methods::is(flight.params)[1] != "Flight Parameters") stop("Flight parameters is not an instance returned from flight.parameters()") + # TODO add a test + if (!is.logical(parallel)) { + stop("parallel has to be TRUE or FALSE") + } # Parameters calculated flight.speed.kmh = flight.params@flight.speed.kmh @@ -90,9 +100,17 @@ litchi.plan = function(roi, output, minBbox = shotGroups::getMinBBox(vertices) # minBbox = getMinBBox(vertices) } - width = minBbox$heigh - height = minBbox$width - alpha = minBbox$angle + + if (parallel == TRUE) { + width = minBbox$height + height = minBbox$width + alpha = minBbox$angle + } else { + width = minBbox$width + height = minBbox$height + alpha = minBbox$angle-90 + } + rads = alpha*pi/180 centroid = apply(minBbox$pts, 2, mean) diff --git a/README.md b/README.md index 535bb7c..b654ce9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -flightplanning-R +# flightplanning-R ================================ [![CRAN](https://www.r-pkg.org/badges/version/flightplanning)](https://cran.r-project.org/web/packages/flightplanning) [![Build Status](https://travis-ci.com/caiohamamura/flightplanning-R.svg)](https://travis-ci.com/caiohamamura/flightplanning-R) @@ -23,6 +23,7 @@ devtools::install_github("caiohamamura/flightplanning-R") * adopts the package to R >= 4.2.0 (backward compatible) - DONE * try to replace {rgdal}, {rgeos} and {sp} with {sf} - WIP +* added `parallel = TRUE | FALSE` parameter, which sets the flying direction over polygon - DONE ## Usage There are two main functions available: diff --git a/man/litchi.plan.Rd b/man/litchi.plan.Rd index 85177eb..b1d8381 100644 --- a/man/litchi.plan.Rd +++ b/man/litchi.plan.Rd @@ -12,7 +12,8 @@ litchi.plan( flight.lines.angle = -1, max.waypoints.distance = 2000, max.flight.time = 15, - starting.point = 1 + starting.point = 1, + parallel = TRUE ) } \arguments{ @@ -34,6 +35,8 @@ default 2000 (some issues have been reported with distances > 2 Km)} time, it will be splitted into smaller missions.} \item{starting.point}{numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1} + +\item{parallel}{logical (default TRUE). Change direction of the fly lines over polygon from parallel to perpendicular} } \value{ A data frame with the waypoints calculated for the flight plan From 31cbbc29d5fc4d248d122535b78ef2dd6bda3556 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Sat, 19 Nov 2022 14:53:32 +0100 Subject: [PATCH 07/35] grid = FALSE --- NAMESPACE | 4 +++- R/main.R | 17 +++++++++-------- man/litchi.plan.Rd | 4 ++-- uav.gpkg | Bin 98304 -> 0 bytes 4 files changed, 14 insertions(+), 11 deletions(-) delete mode 100644 uav.gpkg diff --git a/NAMESPACE b/NAMESPACE index 72eec4a..cc6f4a9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -4,11 +4,13 @@ export(flight.parameters) export(litchi.plan) import(rgdal) import(rgeos) -import(sp) importFrom(grDevices,chull) importFrom(graphics,text) importFrom(methods,slot) importFrom(shotGroups,getMinBBox) +importFrom(sp,Line) +importFrom(sp,Lines) +importFrom(sp,SpatialLines) importFrom(utils,data) importFrom(utils,read.csv) importFrom(utils,write.csv) diff --git a/R/main.R b/R/main.R index c758e2c..80aac70 100644 --- a/R/main.R +++ b/R/main.R @@ -20,7 +20,7 @@ DIAG_35MM = sqrt(36^2 + 24^2) # Classical 35mm film diagonal #' @param max.flight.time maximum flight time. If mission is greater than the estimated #' time, it will be splitted into smaller missions. #' @param starting.point numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 -#' @param parallel logical (default TRUE). Change direction of the fly lines over polygon from parallel to perpendicular +#' @param grid logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular #' #' @note this function will feed the csv flight plan with the `gimbal.pitch.angle` #' and the `photo time interval` for each waypoint, but those are not supported @@ -56,10 +56,11 @@ DIAG_35MM = sqrt(36^2 + 24^2) # Classical 35mm film diagonal #' #' #' @export -#' @import sp rgeos rgdal +#' @import rgeos rgdal #' @importFrom graphics text #' @importFrom shotGroups getMinBBox #' @importFrom methods slot +#' @importFrom sp Line Lines SpatialLines #' @importFrom utils data read.csv write.csv #' litchi.plan = function(roi, @@ -70,7 +71,7 @@ litchi.plan = function(roi, max.waypoints.distance = 2000, max.flight.time = 15, starting.point = 1, - parallel = TRUE) { + grid = FALSE) { # Check parameters if (class(roi)[1] != "SpatialPolygonsDataFrame") stop("ROI is not a valid polygon layer") @@ -79,8 +80,8 @@ litchi.plan = function(roi, if (methods::is(flight.params)[1] != "Flight Parameters") stop("Flight parameters is not an instance returned from flight.parameters()") # TODO add a test - if (!is.logical(parallel)) { - stop("parallel has to be TRUE or FALSE") + if (!is.logical(grid)) { + stop("grid has to be TRUE or FALSE") } # Parameters calculated @@ -101,7 +102,7 @@ litchi.plan = function(roi, # minBbox = getMinBBox(vertices) } - if (parallel == TRUE) { + if (grid == FALSE) { width = minBbox$height height = minBbox$width alpha = minBbox$angle @@ -179,8 +180,8 @@ litchi.plan = function(roi, # RSB glist <- vector(mode="list", length=nrow(waypoints)-1) - for (i in seq_along(glist)) glist[[i]] <- Lines(list(Line(waypoints[c(i, (i+1)),])), ID=as.character(i)) - gLines <- SpatialLines(glist, proj4string=slot(roi, "proj4string")) + for (i in seq_along(glist)) glist[[i]] <- sp::Lines(list(sp::Line(waypoints[c(i, (i+1)),])), ID=as.character(i)) + gLines <- sp::SpatialLines(glist, proj4string=slot(roi, "proj4string")) inter = rgeos::gIntersection(rgeos::gBuffer(roi, width = flightLineDistance), gLines, byid=TRUE) nLines <- length(inter) flightLines <- t(sapply(slot(inter, "lines"), function(x) slot(slot(x, "Lines")[[1]], "coords"))) diff --git a/man/litchi.plan.Rd b/man/litchi.plan.Rd index b1d8381..7b0de90 100644 --- a/man/litchi.plan.Rd +++ b/man/litchi.plan.Rd @@ -13,7 +13,7 @@ litchi.plan( max.waypoints.distance = 2000, max.flight.time = 15, starting.point = 1, - parallel = TRUE + grid = FALSE ) } \arguments{ @@ -36,7 +36,7 @@ time, it will be splitted into smaller missions.} \item{starting.point}{numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1} -\item{parallel}{logical (default TRUE). Change direction of the fly lines over polygon from parallel to perpendicular} +\item{grid}{logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular} } \value{ A data frame with the waypoints calculated for the flight plan diff --git a/uav.gpkg b/uav.gpkg deleted file mode 100644 index bd1a16881fec585cee583f54dae56e7d5f610a7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98304 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 Date: Sat, 19 Nov 2022 17:59:16 +0100 Subject: [PATCH 08/35] readme update --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b654ce9..250674c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ devtools::install_github("caiohamamura/flightplanning-R") * adopts the package to R >= 4.2.0 (backward compatible) - DONE * try to replace {rgdal}, {rgeos} and {sp} with {sf} - WIP -* added `parallel = TRUE | FALSE` parameter, which sets the flying direction over polygon - DONE +* added `grid = FALSE | TRUE` parameter, which sets the flying direction over polygon - DONE ## Usage There are two main functions available: @@ -52,6 +52,7 @@ default 2000 (some issues have been reported with distances > 2 Km) - `max.flight.time`: maximum flight time. If mission is greater than the estimated time, it will be splitted into smaller missions. - `starting.point`: numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 + - `grid`: boolean (FALSE | TRUE). Change the fly direction over polygon from parallel to perpendicular ## Authors - Caio Hamamura From c2f5a96a865886b2566e1bec25d680de1b200691 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Sat, 19 Nov 2022 17:59:16 +0100 Subject: [PATCH 09/35] readme update --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b654ce9..31757cd 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,8 @@ devtools::install_github("caiohamamura/flightplanning-R") * adopts the package to R >= 4.2.0 (backward compatible) - DONE * try to replace {rgdal}, {rgeos} and {sp} with {sf} - WIP -* added `parallel = TRUE | FALSE` parameter, which sets the flying direction over polygon - DONE +* added `grid = FALSE | TRUE` parameter, which sets the flying direction over polygon - DONE +* added input for `sf` polygons -- `roi` can be read with `sf::st_read()` ## Usage There are two main functions available: @@ -52,6 +53,7 @@ default 2000 (some issues have been reported with distances > 2 Km) - `max.flight.time`: maximum flight time. If mission is greater than the estimated time, it will be splitted into smaller missions. - `starting.point`: numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 + - `grid`: boolean (FALSE | TRUE). Change the fly direction over polygon from parallel to perpendicular ## Authors - Caio Hamamura From 3a269dc8e03f6e99707afc1d4d2eaafdafad27e1 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Sat, 19 Nov 2022 21:00:32 +0100 Subject: [PATCH 10/35] a --- R/main.R | 8 +-- R/utils.R | 149 +++++++++++++++++++++++----------------------- man/getMinBBox.Rd | 17 ++++++ 3 files changed, 94 insertions(+), 80 deletions(-) create mode 100644 man/getMinBBox.Rd diff --git a/R/main.R b/R/main.R index 80aac70..f709f16 100644 --- a/R/main.R +++ b/R/main.R @@ -73,6 +73,10 @@ litchi.plan = function(roi, starting.point = 1, grid = FALSE) { # Check parameters + if (class(roi)[1] == "sf") { + roi <- sf::as_Spatial(roi) + } + if (class(roi)[1] != "SpatialPolygonsDataFrame") stop("ROI is not a valid polygon layer") if (length(grep("units=m", as.character(roi@proj4string@projargs))) == 0) @@ -459,7 +463,3 @@ flight.parameters = function( # side.overlap = 0.8, #mudar para overlapFront # front.overlap = 0.8 #mudar para overpSide # )) - - -#TODO -#Make DOUBLE GRID (perpendicular) diff --git a/R/utils.R b/R/utils.R index 915de1b..ba17bb8 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,79 +1,76 @@ -# #' below function is almost copy of shotGroups::getMinBBox() -# #' therefore replaced, GS, 2022-11-18 -# -# #' Rotating calipers algorithm -# #' -# #' @description -# #' Calculates the minimum oriented bounding box using the -# #' rotating calipers algorithm. -# #' Credits go to Daniel Wollschlaeger -# #' -# #' @param xy A matrix of xy values from which to calculate the minimum oriented -# #' bounding box. -# #' -# #' @importFrom grDevices chull -# getMinBBox <- function(xy) { -# stopifnot(is.matrix(xy), is.numeric(xy), nrow(xy) >= 2, ncol(xy) == 2) -# -# ## rotating calipers algorithm using the convex hull -# H <- grDevices::chull(xy) ## hull indices, vertices ordered clockwise -# n <- length(H) ## number of hull vertices -# hull <- xy[H, ] ## hull vertices -# -# ## unit basis vectors for all subspaces spanned by the hull edges -# hDir <- diff(rbind(hull, hull[1, ])) ## hull vertices are circular -# hLens <- sqrt(rowSums(hDir^2)) ## length of basis vectors -# huDir <- diag(1/hLens) %*% hDir ## scaled to unit length -# -# ## unit basis vectors for the orthogonal subspaces -# ## rotation by 90 deg -> y' = x, x' = -y -# ouDir <- cbind(-huDir[ , 2], huDir[ , 1]) -# -# ## project hull vertices on the subspaces spanned by the hull edges, and on -# ## the subspaces spanned by their orthogonal complements - in subspace coords -# projMat <- rbind(huDir, ouDir) %*% t(hull) -# -# ## range of projections and corresponding width/height of bounding rectangle -# rangeH <- matrix(numeric(n*2), ncol=2) ## hull edge -# rangeO <- matrix(numeric(n*2), ncol=2) ## orthogonal subspace -# widths <- numeric(n) -# heights <- numeric(n) -# -# for(i in seq(along=numeric(n))) { -# rangeH[i, ] <- range(projMat[ i, ]) -# -# ## the orthogonal subspace is in the 2nd half of the matrix -# rangeO[i, ] <- range(projMat[n+i, ]) -# widths[i] <- abs(diff(rangeH[i, ])) -# heights[i] <- abs(diff(rangeO[i, ])) -# } -# -# ## extreme projections for min-area rect in subspace coordinates -# ## hull edge leading to minimum-area -# eMin <- which.min(widths*heights) -# hProj <- rbind( rangeH[eMin, ], 0) -# oProj <- rbind(0, rangeO[eMin, ]) -# -# ## move projections to rectangle corners -# hPts <- sweep(hProj, 1, oProj[ , 1], "+") -# oPts <- sweep(hProj, 1, oProj[ , 2], "+") -# -# ## corners in standard coordinates, rows = x,y, columns = corners -# ## in combined (4x2)-matrix: reverse point order to be usable in polygon() -# ## basis formed by hull edge and orthogonal subspace -# basis <- cbind(huDir[eMin, ], ouDir[eMin, ]) -# hCorn <- basis %*% hPts -# oCorn <- basis %*% oPts -# pts <- t(cbind(hCorn, oCorn[ , c(2, 1)])) -# -# ## angle of longer edge pointing up -# dPts <- diff(pts) -# e <- dPts[which.max(rowSums(dPts^2)), ] ## one of the longer edges -# eUp <- e * sign(e[2]) ## rotate upwards 180 deg if necessary -# deg <- atan2(eUp[2], eUp[1])*180 / pi ## angle in degrees -# -# return(list(pts=pts, width=heights[eMin], height=widths[eMin], angle=deg)) -# } +#' Rotating calipers algorithm +#' +#' @description +#' Calculates the minimum oriented bounding box using the +#' rotating calipers algorithm. +#' Credits go to Daniel Wollschlaeger +#' +#' @param xy A matrix of xy values from which to calculate the minimum oriented +#' bounding box. +#' +#' @importFrom grDevices chull +getMinBBox <- function(xy) { + stopifnot(is.matrix(xy), is.numeric(xy), nrow(xy) >= 2, ncol(xy) == 2) + + ## rotating calipers algorithm using the convex hull + H <- grDevices::chull(xy) ## hull indices, vertices ordered clockwise + n <- length(H) ## number of hull vertices + hull <- xy[H, ] ## hull vertices + + ## unit basis vectors for all subspaces spanned by the hull edges + hDir <- diff(rbind(hull, hull[1, ])) ## hull vertices are circular + hLens <- sqrt(rowSums(hDir^2)) ## length of basis vectors + huDir <- diag(1/hLens) %*% hDir ## scaled to unit length + + ## unit basis vectors for the orthogonal subspaces + ## rotation by 90 deg -> y' = x, x' = -y + ouDir <- cbind(-huDir[ , 2], huDir[ , 1]) + + ## project hull vertices on the subspaces spanned by the hull edges, and on + ## the subspaces spanned by their orthogonal complements - in subspace coords + projMat <- rbind(huDir, ouDir) %*% t(hull) + + ## range of projections and corresponding width/height of bounding rectangle + rangeH <- matrix(numeric(n*2), ncol=2) ## hull edge + rangeO <- matrix(numeric(n*2), ncol=2) ## orthogonal subspace + widths <- numeric(n) + heights <- numeric(n) + + for(i in seq(along=numeric(n))) { + rangeH[i, ] <- range(projMat[ i, ]) + + ## the orthogonal subspace is in the 2nd half of the matrix + rangeO[i, ] <- range(projMat[n+i, ]) + widths[i] <- abs(diff(rangeH[i, ])) + heights[i] <- abs(diff(rangeO[i, ])) + } + + ## extreme projections for min-area rect in subspace coordinates + ## hull edge leading to minimum-area + eMin <- which.min(widths*heights) + hProj <- rbind( rangeH[eMin, ], 0) + oProj <- rbind(0, rangeO[eMin, ]) + + ## move projections to rectangle corners + hPts <- sweep(hProj, 1, oProj[ , 1], "+") + oPts <- sweep(hProj, 1, oProj[ , 2], "+") + + ## corners in standard coordinates, rows = x,y, columns = corners + ## in combined (4x2)-matrix: reverse point order to be usable in polygon() + ## basis formed by hull edge and orthogonal subspace + basis <- cbind(huDir[eMin, ], ouDir[eMin, ]) + hCorn <- basis %*% hPts + oCorn <- basis %*% oPts + pts <- t(cbind(hCorn, oCorn[ , c(2, 1)])) + + ## angle of longer edge pointing up + dPts <- diff(pts) + e <- dPts[which.max(rowSums(dPts^2)), ] ## one of the longer edges + eUp <- e * sign(e[2]) ## rotate upwards 180 deg if necessary + deg <- atan2(eUp[2], eUp[1])*180 / pi ## angle in degrees + + return(list(pts=pts, width=heights[eMin], height=widths[eMin], angle=deg)) +} #' Provided an angle, calculate the corresponding minimum bounding box #' diff --git a/man/getMinBBox.Rd b/man/getMinBBox.Rd new file mode 100644 index 0000000..afb010d --- /dev/null +++ b/man/getMinBBox.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{getMinBBox} +\alias{getMinBBox} +\title{Rotating calipers algorithm} +\usage{ +getMinBBox(xy) +} +\arguments{ +\item{xy}{A matrix of xy values from which to calculate the minimum oriented +bounding box.} +} +\description{ +Calculates the minimum oriented bounding box using the +rotating calipers algorithm. +Credits go to Daniel Wollschlaeger +} From dab70da586114592438f4e8750583ea8252f4557 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Sat, 19 Nov 2022 21:28:18 +0100 Subject: [PATCH 11/35] distance --- R/main.R | 3 +++ 1 file changed, 3 insertions(+) diff --git a/R/main.R b/R/main.R index f709f16..cc43aaa 100644 --- a/R/main.R +++ b/R/main.R @@ -355,6 +355,9 @@ because the total time would be ", round(totalFlightTime, 2), " minutes.") message(round(alpha, 4)) message('Total flight time: ', appendLF = FALSE) message(round(totalFlightTime, 4)) + message('Distance between flyin lines: ', appendLF = FALSE) + message(round(flightLineDistance, 3)) + message('', appendLF = TRUE) return (waypoints) } From 8cebb352d34dd8a4ecbe082f4ee30513245ca6a0 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Sun, 20 Nov 2022 21:16:22 +0100 Subject: [PATCH 12/35] litchi_sf() --- .Rbuildignore | 1 + NAMESPACE | 2 + R/litchi_sf.R | 359 ++++++++++++++++++++++++++++++++++++ README.md | 92 +++++++++- man/litchi_sf.Rd | 79 ++++++++ mytest/fly.csv | 45 +++++ mytest/fly1.csv | 47 +++++ mytest/fly2.csv | 55 ++++++ mytest/fly_entire.csv | 101 +++++++++++ mytest/lasek.gpkg | Bin 0 -> 106496 bytes mytest/outbreaks.R | 25 +++ mytest/test_sf.R | 412 ++++++++++++++++++++++++++++++++++++++++++ mytest/uav.R | 412 ++++++++++++++++++++++++++++++++++++++++++ mytest/uav.gpkg | Bin 0 -> 98304 bytes mytest/uav_bug.gpkg | Bin 0 -> 143360 bytes mytest/uav_test.R | 50 +++++ tests/testthat/test.R | 54 +++--- 17 files changed, 1704 insertions(+), 30 deletions(-) create mode 100644 R/litchi_sf.R create mode 100644 man/litchi_sf.Rd create mode 100644 mytest/fly.csv create mode 100644 mytest/fly1.csv create mode 100644 mytest/fly2.csv create mode 100644 mytest/fly_entire.csv create mode 100644 mytest/lasek.gpkg create mode 100644 mytest/outbreaks.R create mode 100644 mytest/test_sf.R create mode 100644 mytest/uav.R create mode 100644 mytest/uav.gpkg create mode 100644 mytest/uav_bug.gpkg create mode 100644 mytest/uav_test.R diff --git a/.Rbuildignore b/.Rbuildignore index 05deeae..d854517 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -3,3 +3,4 @@ ^\.travis\.yml$ ^oledlg\.dll$ ^README.md$ +^mytest$ diff --git a/NAMESPACE b/NAMESPACE index cc6f4a9..f05ea2d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,8 +2,10 @@ export(flight.parameters) export(litchi.plan) +export(litchi_sf) import(rgdal) import(rgeos) +import(sf) importFrom(grDevices,chull) importFrom(graphics,text) importFrom(methods,slot) diff --git a/R/litchi_sf.R b/R/litchi_sf.R new file mode 100644 index 0000000..548d7ac --- /dev/null +++ b/R/litchi_sf.R @@ -0,0 +1,359 @@ +#' Function to generate Litchi csv flight plan +#' +#' @rdname litchi_sf +#' +#' @return A data frame with the waypoints calculated for the flight plan +#' +#' @param roi range of interest loaded as an OGR layer, must be in +#' a metric units projection for working properly +#' @param output output path for the csv file +#' @param flight.params Flight Parameters. parameters calculated from flight.parameters() +#' @param gimbal.pitch.angle gimbal angle for taking photos, default -90 (overriden at flight time) +#' @param flight.lines.angle angle for the flight lines, default -1 (auto set based on larger direction) +#' @param max.waypoints.distance maximum distance between waypoints in meters, +#' default 2000 (some issues have been reported with distances > 2 Km) +#' @param max.flight.time maximum flight time. If mission is greater than the estimated +#' time, it will be splitted into smaller missions. +#' @param starting.point numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 +#' @param grid logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular +#' +#' @note this function will feed the csv flight plan with the `gimbal.pitch.angle` +#' and the `photo time interval` for each waypoint, but those are not supported +#' by Litchi yet, although they are present in the exported csv from the +#' Litchi hub platform, though it may be supported in the future; when it does +#' the function will already work with this feature. +#' +#' @examples +#' library(flightplanning) +#' +#' exampleBoundary = +#' sf::st_read( +#' system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> +#' sf::st_as_sf() +#' +#' outPath = tempfile(fileext=".csv") +#' +#' flight.params = flightplanning::flight.parameters( +#' gsd = 4, +#' side.overlap = 0.8, +#' front.overlap = 0.8, +#' flight.speed.kmh = 54 +#' ) +#' +#' litchi_sf(exampleBoundary, +#' outPath, +#' flight.params, +#' flight.lines.angle = -1, +#' max.waypoints.distance = 2000, +#' max.flight.time = 15) +#' +#' +#' @import sf +#' @importFrom graphics text +#' @importFrom shotGroups getMinBBox +#' @importFrom methods slot +#' @importFrom sp Line Lines SpatialLines +#' @importFrom utils data read.csv write.csv +#' +#' @export +litchi_sf = function(roi, + output, + flight.params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + grid = FALSE) { + + # Check parameters + if (class(roi)[1] != "sf") { + roi <- sf::st_as_sf(roi) + } + + if(nrow(roi) > 1) { + roi <- roi[1,] |> + sf::st_as_sf() + } + + roiCRS <- sf::st_crs(roi) + + if (!grepl("LENGTHUNIT[\"metre\",1]", sf::st_crs(roi)[2], fixed = TRUE)) + stop("ROI is not in a metric projection") + if (methods::is(flight.params)[1] != "Flight Parameters") + stop("Flight parameters is not an instance returned from flight.parameters()") + if (!is.logical(grid)) { + stop("grid has to be TRUE or FALSE") + } + + # Parameters calculated + flight.speed.kmh = flight.params@flight.speed.kmh + flightSpeedMs = flight.speed.kmh / 3.6 + height = flight.params@height + groundHeight = flight.params@ground.height + groundHeightOverlap = groundHeight * flight.params@front.overlap + flightLineDistance = flight.params@flight.line.distance + vertices <- sf::st_coordinates(roi)[,1:2] + + # Get bounding box parameters + if (flight.lines.angle != -1) { + minBbox = getBBoxAngle(vertices, flight.lines.angle) + } else { + # if angle not specified use minimum possible bounding box + minBbox = shotGroups::getMinBBox(vertices) + } + + if (grid == FALSE) { + width = minBbox$height + height = minBbox$width + alpha = minBbox$angle + } else { + width = minBbox$width + height = minBbox$height + alpha = minBbox$angle-90 + } + + rads = alpha*pi/180 + centroid = apply(minBbox$pts, 2, mean) + + # Calculate points offset from centroid + # based on angle and width/height offsets + # width offsets (between flightlines) + nLines = ceiling(width / flightLineDistance) + 1 + xWidths = (-nLines/2):(nLines/2) * flightLineDistance + xWidths = rep(xWidths, each=2) + + # heights offset (one for upper half + # one for lower half) + heightDistance = groundHeight-groundHeightOverlap + heightAdjusted = height + 2*heightDistance + # Put offset to avoid intersection issues + heightAdjusted = heightAdjusted + heightDistance*2 + heightMHalf = -heightAdjusted/2 + heightPHalf = heightAdjusted/2 + yHeights = c(heightMHalf, heightPHalf) + + + # Switch position of the first point + if (starting.point == 2) { + yHeights = c(heightPHalf, heightMHalf) + } else if (starting.point == 3) { + xWidths = rev(xWidths) + yHeights = c(heightPHalf, heightMHalf) + } else if (starting.point == 4) { + xWidths = rev(xWidths) + } + + # Interleave one upper, two bottom + # two upper, two bottom... until end + yHeights = c(rep(c(yHeights, rev(yHeights)), nLines/2+1)) + yHeights = yHeights[1:length(xWidths)] + + + # Calculate translated x and y from + # angles and offsets from centroid + xys = data.frame( + x = -xWidths * sin(rads) + + yHeights * cos(rads), + y = xWidths * cos(rads) + + yHeights * sin(rads)) + + # Initial waypoints to intersect waypoints + waypoints = xys + rep(centroid, each=nrow(xys)) + + ################################################# + # Intersect each flight line from bounding box + # to match actual ROI + ################################################# + # For some reason gIntersection with MULTILINESTRING + # will return linestrings in inconsistent order + # though it will be done in a for loop + + # --------------------------------------------------------------------------------------------- + lines <- do.call( + sf::st_sfc, + lapply( + 1:(nrow(waypoints)-1), + function(i) { + sf::st_linestring( + matrix( + c(as.numeric(waypoints[i, ]), as.numeric(waypoints[i + 1, ])), + ncol = 2, byrow = TRUE + ) + ) + } + ) + ) + + lines <- lines |> + sf::st_as_sf(crs = roiCRS) + lines$ID <- seq.int(nrow(lines)) + + # --------------------------------------------------------------------------------------------- + inter <-suppressWarnings(sf::st_intersection( + sf::st_buffer(sf::st_as_sf(roi), flightLineDistance), + lines) |> + sf::st_cast(to = "LINESTRING")) + + # --------------------------------------------------------------------------------------------- + # nLines <- length(inter) + # gflightLines <- + waypoints <- sf::st_coordinates(inter)[,1:2] + + # --------------------------------------------------------------------------------------------- + + # Calculate curves points to allow smooth curves + curvedPoints = outerCurvePoints(waypoints = waypoints, + angle = alpha, + flightLineDistance = flightLineDistance) + + # Adjust curve points position to avoid acute angles + adjustedCurves = adjustAcuteAngles(xy = curvedPoints, + angle = alpha, + minAngle = 80) + + # Concatenate regular waypoints with curve waypoints + wptsMatrix = as.data.frame(matrix(nrow=nrow(waypoints)+nrow(adjustedCurves),ncol=4)) + colnames(wptsMatrix) = colnames=c("x", "y", "isCurve", "takePhoto") + mat_pos = 1 + for (i in seq_len(nrow(waypoints))) { + curve = as.vector(adjustedCurves[i, ]) + hasCurve = !anyNA(curve) + if (hasCurve) { + if (curve$before) { + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) + mat_pos = mat_pos + 1 + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + } else { + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) + mat_pos = mat_pos + 1 + } + } else { + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + } + } + waypoints = wptsMatrix + + # Break if distance greater than the maxWaypointDistance + waypointsXY = waypoints[, c("x", "y")] + distances = sqrt(diff(waypoints$x)**2 + diff(waypoints$y)**2) + breakIdx = distances > max.waypoints.distance + + newSize = nrow(waypoints) + sum(breakIdx) + if (newSize != nrow(waypoints)) { + midpoints = (waypointsXY[breakIdx,] + waypointsXY[-1,][breakIdx,])/2 + waypoints2 = data.frame(x = numeric(newSize), + y = numeric(newSize), + isCurve = FALSE, + takePhoto = TRUE) + + pos = seq_along(breakIdx)[breakIdx] + idx = pos + order(pos) + waypoints2[idx,1:2] = midpoints + waypoints2[-idx,] = waypoints + waypoints = waypoints2 + } +waypoints + + # --------------------------------------------------------------------------------------------- + t <- waypoints |> + sf::st_as_sf(coords = c("x", "y"), crs = roiCRS) |> + sf::st_transform(crs = "EPSG:4326") + lngs <- as.numeric(sf::st_coordinates(t)[,1]) + lats <- as.numeric(sf::st_coordinates(t)[,2]) + # --------------------------------------------------------------------------------------------- + + graphics::plot(waypoints[,1:2]) + graphics::polygon(sf::st_coordinates(roi)) + + # Calculate heading + nWaypoints = nrow(waypoints) + latDiff = lats[-1]-lats[-nWaypoints] + lngDiff = lngs[-1]-lngs[-nWaypoints] + headDegree = atan(latDiff/lngDiff)/pi*180 + finalHeading = 270-headDegree + finalHeading[lngDiff > 0] = 90-headDegree[lngDiff > 0] + + # Set parameters of the flight in the CSV + dfLitchi = read.csv(system.file("extdata/litchi.csv", package = "flightplanning")) + dfLitchi = dfLitchi[rep(1, length(lats)),] + dfLitchi$latitude = lats + dfLitchi$longitude = lngs + dfLitchi$altitude.m. = flight.params@height + dfLitchi$speed.m.s. = flightSpeedMs + dfLitchi$heading.deg. = c(finalHeading, 90) + dfLitchi$curvesize.m. = 0 + dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 + dfLitchi$photo_distinterval = flight.params@ground.height + dfLitchi$gimbalpitchangle = gimbal.pitch.angle + + + # Split the flight if is too long + dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) + distAcum = c(0,cumsum(dists)) + flightTime = distAcum / (flightSpeedMs*0.75) / 60 + finalSize = nrow(dfLitchi) + totalFlightTime = flightTime[finalSize] + dfLitchi$split = 1 + if (totalFlightTime > max.flight.time) { + indexes = seq_len(finalSize) + nBreaks = ceiling(totalFlightTime/max.flight.time) + breaks = seq(0, flightTime[finalSize], length.out = nBreaks+1)[c(-1, -nBreaks-1)] + endWaypointsIndex = indexes[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] + endWaypoints = flightTime[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] + selected = sapply(breaks, function(x) which.min(abs(endWaypoints-x))) + waypointsBreak = endWaypointsIndex[indexes[selected]] + + + dfLitchi$split = rep(1:nBreaks, diff(c(0, waypointsBreak, finalSize))) + splits = split.data.frame(dfLitchi, f = dfLitchi$split) + message("Your flight was splitted in ", length(splits), " splits, +because the total time would be ", round(totalFlightTime, 2), " minutes.") + message("They were saved as:") + first = substr(output, 1, nchar(output)-4) + second = substr(output, nchar(output)-3, nchar(output)) + for (dataSplit in splits) { + i = dataSplit[1, ]$split + output2 = paste0(first, i, second) + write.csv(dataSplit[,-ncol(dataSplit)], output2, row.names = FALSE) + message(output2) + } + output2 = paste0(first, "_entire", second) + write.csv(dfLitchi, output2, row.names = FALSE) + message("The entire flight plan was saved as:") + message(output2) + } else { + write.csv(dfLitchi, output, row.names = FALSE) + } + + colors = grDevices::rainbow(length(unique(dfLitchi$split))) + for (i in unique(dfLitchi$split)) + { + graphics::lines(waypoints[dfLitchi$split == i,1:2], lty=2, col=colors[as.integer(i)]) + } + graphics::text(waypoints[,1], waypoints[,2], seq_along(waypoints[,1]), pos=3) + + + message("#####################") + message("## Flight settings ## ") + message("#####################") + message("Min shutter speed: ", appendLF = FALSE) + message(flight.params@minimum.shutter.speed) + message("Photo interval: ", appendLF = FALSE) + message(flight.params@photo.interval, appendLF = FALSE) + message(" s") + message("Flight speed: ", appendLF = FALSE) + message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) + message(" km/h") + message("Flight lines angle: ", appendLF = FALSE) + message(round(alpha, 4)) + message('Total flight time: ', appendLF = FALSE) + message(round(totalFlightTime, 4)) + + return (waypoints) + +} diff --git a/README.md b/README.md index 62324b0..33d2e90 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,97 @@ devtools::install_github("caiohamamura/flightplanning-R") * adopts the package to R >= 4.2.0 (backward compatible) - DONE * added `grid = FALSE | TRUE` parameter, which sets the flying direction over polygon - DONE * added input for `sf` polygons -- `roi` can be read with `sf::st_read()` - DONE -* try to replace {rgdal}, {rgeos} and {sp} with {sf} - WIP +* try to replace {rgdal}, {rgeos} and {sp} with {sf} - DONE? + +## New function for testing + * `litchi_sf()` + +### Example usage + +``` r +library(flightplanning) +f <- "mytest/lasek.gpkg" +roi <- sf::st_read(f) + +output <- "mytest/fly.csv" + +params <- flight.parameters( + height = 120, + focal.length35 = 24, + flight.speed.kmh = 24, + side.overlap = 0.8, + front.overlap = 0.8 +) + +litchi_sf(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = FALSE +) +#> ##################### +#> ## Flight settings ## +#> ##################### +#> Min shutter speed: 1/128 +#> Photo interval: 4 s +#> Flight speed: 23.364 km/h +#> Flight lines angle: 104.3223 +#> Total flight time: 16.2097 +``` + +![](https://i.imgur.com/mG1npRr.png) + +Per default `litchi_sf()` takes first polygon from the input layer. If there is a need to run a mission over several polygons, you can union them like: + +```r +if(nrow(roi) > 1) { + roi <- sf::st_union(roi) +} + +litchi_sf(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = FALSE +) +#> Your flight was splitted in 2 splits, +#> because the total time would be 26.91 minutes. +#> They were saved as: +#> mytest/fly1.csv +#> mytest/fly2.csv +#> The entire flight plan was saved as: +#> mytest/fly_entire.csv +#> ##################### +#> ## Flight settings ## +#> ##################### +#> Min shutter speed: 1/128 +#> Photo interval: 4 s +#> Flight speed: 23.364 km/h +#> Flight lines angle: 104.2442 +#> Total flight time: 26.9055 +``` +![](https://i.imgur.com/Vfr1Bj9.png) + +Using `grid = TRUE` parameter you can change the direction of the grid like: + +```r +litchi_sf(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = TRUE +) +``` +![](https://i.imgur.com/MRFkkrO.png) ## Usage There are two main functions available: diff --git a/man/litchi_sf.Rd b/man/litchi_sf.Rd new file mode 100644 index 0000000..d98d285 --- /dev/null +++ b/man/litchi_sf.Rd @@ -0,0 +1,79 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/litchi_sf.R +\name{litchi_sf} +\alias{litchi_sf} +\title{Function to generate Litchi csv flight plan} +\usage{ +litchi_sf( + roi, + output, + flight.params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + grid = FALSE +) +} +\arguments{ +\item{roi}{range of interest loaded as an OGR layer, must be in +a metric units projection for working properly} + +\item{output}{output path for the csv file} + +\item{flight.params}{Flight Parameters. parameters calculated from flight.parameters()} + +\item{gimbal.pitch.angle}{gimbal angle for taking photos, default -90 (overriden at flight time)} + +\item{flight.lines.angle}{angle for the flight lines, default -1 (auto set based on larger direction)} + +\item{max.waypoints.distance}{maximum distance between waypoints in meters, +default 2000 (some issues have been reported with distances > 2 Km)} + +\item{max.flight.time}{maximum flight time. If mission is greater than the estimated +time, it will be splitted into smaller missions.} + +\item{starting.point}{numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1} + +\item{grid}{logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular} +} +\value{ +A data frame with the waypoints calculated for the flight plan +} +\description{ +Function to generate Litchi csv flight plan +} +\note{ +this function will feed the csv flight plan with the `gimbal.pitch.angle` +and the `photo time interval` for each waypoint, but those are not supported +by Litchi yet, although they are present in the exported csv from the +Litchi hub platform, though it may be supported in the future; when it does +the function will already work with this feature. +} +\examples{ +library(flightplanning) + +exampleBoundary = + sf::st_read( + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> + sf::st_as_sf() + +outPath = tempfile(fileext=".csv") + +flight.params = flightplanning::flight.parameters( + gsd = 4, + side.overlap = 0.8, + front.overlap = 0.8, + flight.speed.kmh = 54 +) + +litchi_sf(exampleBoundary, + outPath, + flight.params, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15) + + +} diff --git a/mytest/fly.csv b/mytest/fly.csv new file mode 100644 index 0000000..c56add5 --- /dev/null +++ b/mytest/fly.csv @@ -0,0 +1,45 @@ +"latitude","longitude","altitude.m.","heading.deg.","curvesize.m.","rotationdir","gimbalmode","gimbalpitchangle","actiontype1","actionparam1","actiontype2","actionparam2","actiontype3","actionparam3","actiontype4","actionparam4","actiontype5","actionparam5","actiontype6","actionparam6","actiontype7","actionparam7","actiontype8","actionparam8","actiontype9","actionparam9","actiontype10","actionparam10","actiontype11","actionparam11","actiontype12","actionparam12","actiontype13","actionparam13","actiontype14","actionparam14","actiontype15","actionparam15","altitudemode","speed.m.s.","poi_latitude","poi_longitude","poi_altitude.m.","poi_altitudemode","photo_timeinterval","photo_distinterval","split" +51.2919683460086,16.7790764766696,120,335.344305730144,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2922674513242,16.7789391837187,120,335.343070014653,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.294387870021,16.7779658305699,120,335.340903877391,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2965082788128,16.7769923849096,120,335.33961721376,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.296906602515,16.7768095100576,120,259.770773559568,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2968205096737,16.7763324226227,120,155.33907903129,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2965214080054,16.7764697469346,120,155.34036570435,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2943018148904,16.777488751769,120,155.342633211142,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2920822109213,16.7785076552323,120,155.343919602709,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2917831061306,16.7786449503826,120,259.770674256399,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2916970213075,16.778167912112,120,335.343360675317,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.292136184409,16.7779663221765,120,335.342092854172,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2941793131558,16.7770284058784,120,335.340005593834,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2962224327054,16.776090403686,120,335.338809060882,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2965215339236,16.7759530778745,120,259.770255586602,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2964354376542,16.7754759961221,120,155.338510690194,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2957661890451,16.7757832712683,120,155.339762243108,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2939855385774,16.7766007822905,120,155.341581385563,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2922048811234,16.7774182280686,120,155.34264364643,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2919057777075,16.7775555306524,120,259.77013988513,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2918196884549,16.7770784932932,120,335.342077200383,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2922735801454,16.7768701301534,120,335.340130469702,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2956308627014,16.7753288007202,120,335.338262634681,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2959299630151,16.7751914718701,120,259.769744308496,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2958438637255,16.7747143977402,120,155.337896001058,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2953082839613,16.7749603078612,120,155.339705081646,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2923025575309,16.7763402647906,120,155.341393212261,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2920034554735,16.776477574654,120,259.769606314731,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2919173618406,16.7760005384413,120,335.340875618652,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.292275624933,16.775836065389,120,335.33936163122,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2948812315318,16.7746397882806,120,335.337877759602,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2951803310453,16.7745024573652,120,259.769238157631,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2950942290476,16.7740253923424,120,155.337597778397,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2943890858257,16.7743491615487,120,155.339052412867,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.292246793893,16.7753327372255,120,155.340299529448,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.291947693093,16.7754700534244,120,264.277907440532,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2918982086894,16.774976210477,120,335.339774760004,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2921973088749,16.7748388912434,120,335.338738079298,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2939276241643,16.7740444520593,120,335.337701344326,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.294226723012,16.7739071203345,120,259.768738630692,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2941406187092,16.7734300663346,120,155.337334129579,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2936062829155,16.7736754116343,120,155.338302835104,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2922444068319,16.7743007019532,120,155.33915132502,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2919453073246,16.7744380248159,120,90,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 diff --git a/mytest/fly1.csv b/mytest/fly1.csv new file mode 100644 index 0000000..a3e2cae --- /dev/null +++ b/mytest/fly1.csv @@ -0,0 +1,47 @@ +"latitude","longitude","altitude.m.","heading.deg.","curvesize.m.","rotationdir","gimbalmode","gimbalpitchangle","actiontype1","actionparam1","actiontype2","actionparam2","actiontype3","actionparam3","actiontype4","actionparam4","actiontype5","actionparam5","actiontype6","actionparam6","actiontype7","actionparam7","actiontype8","actionparam8","actiontype9","actionparam9","actiontype10","actionparam10","actiontype11","actionparam11","actiontype12","actionparam12","actiontype13","actionparam13","actiontype14","actionparam14","actiontype15","actionparam15","altitudemode","speed.m.s.","poi_latitude","poi_longitude","poi_altitude.m.","poi_altitudemode","photo_timeinterval","photo_distinterval" +51.2918859381692,16.7792638927025,120,79.8226026966069,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.291971610885,16.7797411217558,120,79.8227816384004,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2920028660525,16.7799152278493,120,79.8230677782738,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2921585788247,16.7807826466846,120,335.457544871711,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.292457803283,16.7806460143841,120,259.823165375639,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2923721341817,16.7801687785165,120,259.822579539039,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2920749986892,16.7785136265937,120,259.821732812637,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2918188321372,16.7770868076956,120,335.453597157208,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2921180520156,16.7769501525706,120,79.8214623148865,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2922037341397,16.7774273794166,120,79.8223090463432,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2926713575421,16.7800321398158,120,79.8231557733601,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2927570272312,16.7805093784983,120,335.4566353831,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.293056250984,16.7803727407798,120,259.823146172011,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.292970580707,16.7798954992825,120,259.821973520838,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2922899602206,16.776104428113,120,259.820326427728,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2918941990096,16.7739003937032,120,335.450006397802,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2921934149075,16.773763717732,120,79.8198423758324,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2922791099608,16.7742409390171,120,79.8207314867395,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2927744889951,16.7769998673352,120,79.822247469864,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2932698036766,16.7797588569164,120,79.8231365691859,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2933554745414,16.7802361012285,120,335.456026331183,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2936546979034,16.7800994598445,120,259.823126967217,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2935690264507,16.7796222127174,120,259.822230427394,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2930688494466,16.776836143912,120,259.82069956577,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2925686068205,16.7740501375791,120,259.819803014126,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2924829109607,16.7735729136876,120,334.288106345404,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.292780903441,16.7734294233984,120,79.8197896544442,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2928665999163,16.7739066500781,120,79.8206871435404,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2933674573644,16.7766960770684,120,79.822219884751,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2938682490293,16.7794855666856,120,79.8231173645987,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2939539210699,16.7799628166276,120,335.455417256986,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2942531440411,16.7798261715779,120,259.823107758778,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2941674714125,16.7793489188209,120,259.822209341555,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2936660650782,16.7765560083517,120,259.820674721284,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2931645927994,16.7737631606639,120,259.819776289951,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2930788957085,16.7732859311959,120,334.293782069667,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2933768944397,16.7731424742594,120,79.8197629475071,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2934625921461,16.7736197065158,120,79.8206623142941,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2939646760181,16.7764159568664,120,79.8221988031833,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.294466693785,16.7792122701522,120,79.8230981580941,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2945523670015,16.7796895257243,120,335.455382859064,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2948515901835,16.7795528803611,120,259.823088556083,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2947659163791,16.779075621974,120,259.822188269372,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2942632895276,16.7762759189563,120,259.820649919111,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2937605964109,16.7734762790273,120,259.819749619906,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2936748980892,16.7729990439823,120,334.280336626995,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 diff --git a/mytest/fly2.csv b/mytest/fly2.csv new file mode 100644 index 0000000..faecfa4 --- /dev/null +++ b/mytest/fly2.csv @@ -0,0 +1,55 @@ +"latitude","longitude","altitude.m.","heading.deg.","curvesize.m.","rotationdir","gimbalmode","gimbalpitchangle","actiontype1","actionparam1","actiontype2","actionparam2","actiontype3","actionparam3","actiontype4","actionparam4","actiontype5","actionparam5","actiontype6","actionparam6","actiontype7","actionparam7","actiontype8","actionparam8","actiontype9","actionparam9","actiontype10","actionparam10","actiontype11","actionparam11","actiontype12","actionparam12","actiontype13","actionparam13","actiontype14","actionparam14","actiontype15","actionparam15","altitudemode","speed.m.s.","poi_latitude","poi_longitude","poi_altitude.m.","poi_altitudemode","photo_timeinterval","photo_distinterval" +51.2939728828231,16.7728555076417,120,79.8197362346309,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2940585817605,16.7733327454749,120,79.8206374803493,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2945618934832,16.7761358270964,120,79.8221777210741,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2950651387778,16.7789389719627,120,79.8230789516217,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2951508131701,16.779416233165,120,335.454773740764,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2954500359612,16.7792795841359,120,259.823069349891,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.295364360981,16.7788023201184,120,259.82216717171,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2948604968636,16.7759957312961,120,259.82062503911,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.294356566155,16.7731892058748,120,259.819722849058,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2942708666018,16.7727119652533,120,335.447472191989,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2945700808179,16.7725752740545,120,79.8197515492192,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2946808287884,16.7731920051858,120,79.8214248224937,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2956635829888,16.778665666441,120,79.8230597481903,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2957492585569,16.7791429332737,120,335.454164598128,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2960484809571,16.7790062805784,120,259.823050143287,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.295962804801,16.7785290109305,120,259.821533720513,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2950575011417,16.7734866720519,120,259.820017274852,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2949718027152,16.7730094236078,120,335.447447163395,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2952710172436,16.7728727321083,120,79.8201256746835,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2954338502843,16.7737795475972,120,79.821642125784,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2962620264178,16.7783923535868,120,79.8230405402142,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2963477031618,16.77886962605,120,335.453555427462,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2966469251711,16.7787329696884,120,259.823030936418,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2965612478392,16.7782556944098,120,259.821748506218,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2958088746835,16.7740650539091,120,259.820466063626,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2957231785172,16.7735877965628,120,335.447709679127,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2960223937042,16.7734511064195,120,79.8205724380697,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2961838984207,16.7743505648026,120,79.8218548825766,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2968604657419,16.7781190148882,120,79.8230213204257,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2969461436619,16.778596292982,120,335.451567772658,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2972453638368,16.7784596249126,120,259.823011710385,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2971596853287,16.7779823440034,120,259.821961262636,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2965589214959,16.7746360802778,120,259.82091080495,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2964732275603,16.7741588140573,120,335.447964686588,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2967724433967,16.774022125227,120,79.82101718261,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2969339439092,16.7749216003349,120,79.8215722753866,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2971352039346,16.776042577994,120,79.8225067356959,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2975445826598,16.7783229485683,120,335.452062726937,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2978438035999,16.7781862832734,120,259.822992490688,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2977581239158,16.7777089967333,120,259.82247735627,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2975071907525,16.7763112227912,120,259.821962222233,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2974215035026,16.7758339434543,120,335.449349965291,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2977207213489,16.7756972624499,120,79.8220059768095,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.29784128424,16.7763688026083,120,79.8225211139331,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.298057342916,16.777572320348,120,79.8229828793043,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2981430231882,16.7780496097036,120,335.448258727057,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2984422403918,16.7779129221047,120,259.822973261424,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2983565595314,16.7774356299336,120,259.822564867824,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2981753776916,16.7764263831872,120,259.822156473261,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2980896908331,16.775949096727,120,335.449138371802,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.298388908767,16.7758124143469,120,79.8222690951576,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2985544717075,16.7767346309419,120,79.8225702846434,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2985857255846,16.7769087261549,120,79.8227492393285,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 +51.2986714086076,16.7773860196425,120,90,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 diff --git a/mytest/fly_entire.csv b/mytest/fly_entire.csv new file mode 100644 index 0000000..06c15e1 --- /dev/null +++ b/mytest/fly_entire.csv @@ -0,0 +1,101 @@ +"latitude","longitude","altitude.m.","heading.deg.","curvesize.m.","rotationdir","gimbalmode","gimbalpitchangle","actiontype1","actionparam1","actiontype2","actionparam2","actiontype3","actionparam3","actiontype4","actionparam4","actiontype5","actionparam5","actiontype6","actionparam6","actiontype7","actionparam7","actiontype8","actionparam8","actiontype9","actionparam9","actiontype10","actionparam10","actiontype11","actionparam11","actiontype12","actionparam12","actiontype13","actionparam13","actiontype14","actionparam14","actiontype15","actionparam15","altitudemode","speed.m.s.","poi_latitude","poi_longitude","poi_altitude.m.","poi_altitudemode","photo_timeinterval","photo_distinterval","split" +51.2918859381692,16.7792638927025,120,79.8226026966069,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.291971610885,16.7797411217558,120,79.8227816384004,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2920028660525,16.7799152278493,120,79.8230677782738,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2921585788247,16.7807826466846,120,335.457544871711,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.292457803283,16.7806460143841,120,259.823165375639,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2923721341817,16.7801687785165,120,259.822579539039,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2920749986892,16.7785136265937,120,259.821732812637,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2918188321372,16.7770868076956,120,335.453597157208,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2921180520156,16.7769501525706,120,79.8214623148865,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2922037341397,16.7774273794166,120,79.8223090463432,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2926713575421,16.7800321398158,120,79.8231557733601,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2927570272312,16.7805093784983,120,335.4566353831,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.293056250984,16.7803727407798,120,259.823146172011,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.292970580707,16.7798954992825,120,259.821973520838,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2922899602206,16.776104428113,120,259.820326427728,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2918941990096,16.7739003937032,120,335.450006397802,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2921934149075,16.773763717732,120,79.8198423758324,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2922791099608,16.7742409390171,120,79.8207314867395,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2927744889951,16.7769998673352,120,79.822247469864,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2932698036766,16.7797588569164,120,79.8231365691859,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2933554745414,16.7802361012285,120,335.456026331183,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2936546979034,16.7800994598445,120,259.823126967217,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2935690264507,16.7796222127174,120,259.822230427394,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2930688494466,16.776836143912,120,259.82069956577,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2925686068205,16.7740501375791,120,259.819803014126,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2924829109607,16.7735729136876,120,334.288106345404,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.292780903441,16.7734294233984,120,79.8197896544442,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2928665999163,16.7739066500781,120,79.8206871435404,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2933674573644,16.7766960770684,120,79.822219884751,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2938682490293,16.7794855666856,120,79.8231173645987,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2939539210699,16.7799628166276,120,335.455417256986,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2942531440411,16.7798261715779,120,259.823107758778,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2941674714125,16.7793489188209,120,259.822209341555,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2936660650782,16.7765560083517,120,259.820674721284,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2931645927994,16.7737631606639,120,259.819776289951,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2930788957085,16.7732859311959,120,334.293782069667,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2933768944397,16.7731424742594,120,79.8197629475071,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2934625921461,16.7736197065158,120,79.8206623142941,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2939646760181,16.7764159568664,120,79.8221988031833,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.294466693785,16.7792122701522,120,79.8230981580941,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2945523670015,16.7796895257243,120,335.455382859064,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2948515901835,16.7795528803611,120,259.823088556083,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2947659163791,16.779075621974,120,259.822188269372,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2942632895276,16.7762759189563,120,259.820649919111,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2937605964109,16.7734762790273,120,259.819749619906,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2936748980892,16.7729990439823,120,334.280336626995,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 +51.2939728828231,16.7728555076417,120,79.8197362346309,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2940585817605,16.7733327454749,120,79.8206374803493,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2945618934832,16.7761358270964,120,79.8221777210741,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2950651387778,16.7789389719627,120,79.8230789516217,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2951508131701,16.779416233165,120,335.454773740764,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2954500359612,16.7792795841359,120,259.823069349891,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.295364360981,16.7788023201184,120,259.82216717171,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2948604968636,16.7759957312961,120,259.82062503911,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.294356566155,16.7731892058748,120,259.819722849058,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2942708666018,16.7727119652533,120,335.447472191989,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2945700808179,16.7725752740545,120,79.8197515492192,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2946808287884,16.7731920051858,120,79.8214248224937,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2956635829888,16.778665666441,120,79.8230597481903,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2957492585569,16.7791429332737,120,335.454164598128,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2960484809571,16.7790062805784,120,259.823050143287,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.295962804801,16.7785290109305,120,259.821533720513,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2950575011417,16.7734866720519,120,259.820017274852,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2949718027152,16.7730094236078,120,335.447447163395,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2952710172436,16.7728727321083,120,79.8201256746835,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2954338502843,16.7737795475972,120,79.821642125784,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2962620264178,16.7783923535868,120,79.8230405402142,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2963477031618,16.77886962605,120,335.453555427462,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2966469251711,16.7787329696884,120,259.823030936418,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2965612478392,16.7782556944098,120,259.821748506218,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2958088746835,16.7740650539091,120,259.820466063626,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2957231785172,16.7735877965628,120,335.447709679127,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2960223937042,16.7734511064195,120,79.8205724380697,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2961838984207,16.7743505648026,120,79.8218548825766,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2968604657419,16.7781190148882,120,79.8230213204257,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2969461436619,16.778596292982,120,335.451567772658,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2972453638368,16.7784596249126,120,259.823011710385,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2971596853287,16.7779823440034,120,259.821961262636,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2965589214959,16.7746360802778,120,259.82091080495,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2964732275603,16.7741588140573,120,335.447964686588,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2967724433967,16.774022125227,120,79.82101718261,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2969339439092,16.7749216003349,120,79.8215722753866,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2971352039346,16.776042577994,120,79.8225067356959,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2975445826598,16.7783229485683,120,335.452062726937,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2978438035999,16.7781862832734,120,259.822992490688,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2977581239158,16.7777089967333,120,259.82247735627,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2975071907525,16.7763112227912,120,259.821962222233,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2974215035026,16.7758339434543,120,335.449349965291,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2977207213489,16.7756972624499,120,79.8220059768095,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.29784128424,16.7763688026083,120,79.8225211139331,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.298057342916,16.777572320348,120,79.8229828793043,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2981430231882,16.7780496097036,120,335.448258727057,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2984422403918,16.7779129221047,120,259.822973261424,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2983565595314,16.7774356299336,120,259.822564867824,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2981753776916,16.7764263831872,120,259.822156473261,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2980896908331,16.775949096727,120,335.449138371802,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.298388908767,16.7758124143469,120,79.8222690951576,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2985544717075,16.7767346309419,120,79.8225702846434,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2985857255846,16.7769087261549,120,79.8227492393285,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 +51.2986714086076,16.7773860196425,120,90,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 diff --git a/mytest/lasek.gpkg b/mytest/lasek.gpkg new file mode 100644 index 0000000000000000000000000000000000000000..e313b637b773768c4ce0a5c04a75aec964a01e7b GIT binary patch literal 106496 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 1) { + roi <- sf::st_union(roi) |> + sf::st_as_sf() +} + +if(nrow(roi) > 1) { + roi <- roi[1,] |> + sf::st_as_sf() +} + + +params = flightplanning::flight.parameters(height=Wysokosc, + flight.speed.kmh=24, + side.overlap = 0.8, + front.overlap = 0.8) + +flight.params <- params +gimbal.pitch.angle = -90 +flight.lines.angle = -1 +max.waypoints.distance = 2000 +max.flight.time = 15 +starting.point = 1 +grid = FALSE + +flightplanning::litchi_sf(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + grid = FALSE +) + + +# --------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------- +#' Function to generate Litchi csv flight plan +#' +#' @rdname litchi.sf +#' +#' @return A data frame with the waypoints calculated for the flight plan +#' +#' @param roi range of interest loaded as an OGR layer, must be in +#' a metric units projection for working properly +#' @param output output path for the csv file +#' @param flight.params Flight Parameters. parameters calculated from flight.parameters() +#' @param gimbal.pitch.angle gimbal angle for taking photos, default -90 (overriden at flight time) +#' @param flight.lines.angle angle for the flight lines, default -1 (auto set based on larger direction) +#' @param max.waypoints.distance maximum distance between waypoints in meters, +#' default 2000 (some issues have been reported with distances > 2 Km) +#' @param max.flight.time maximum flight time. If mission is greater than the estimated +#' time, it will be splitted into smaller missions. +#' @param starting.point numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 +#' @param grid logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular +#' +#' @note this function will feed the csv flight plan with the `gimbal.pitch.angle` +#' and the `photo time interval` for each waypoint, but those are not supported +#' by Litchi yet, although they are present in the exported csv from the +#' Litchi hub platform, though it may be supported in the future; when it does +#' the function will already work with this feature. +#' +#' @examples +#' library(flightplanning) +#' +#' exampleBoundary = sf::st_read( +#' system.file("extdata", +#' "exampleBoundary.shp", +#' package="flightplanning" +#' ), +#' "exampleBoundary") +#' outPath = tempfile(fileext=".csv") +#' +#' flight.params = flight.parameters( +#' gsd = 4, +#' side.overlap = 0.8, +#' front.overlap = 0.8, +#' flight.speed.kmh = 54 +#' ) +#' +#' lichi.sf(exampleBoundary, +#' outPath, +#' flight.params, +#' flight.lines.angle = -1, +#' max.waypoints.distance = 2000, +#' max.flight.time = 15) +#' +#' +#' @export +#' @import sf +#' @importFrom graphics text +#' @importFrom shotGroups getMinBBox +#' @importFrom methods slot +#' @importFrom sp Line Lines SpatialLines +#' @importFrom utils data read.csv write.csv +#' +litchi_sf = function(roi, + output, + flight.params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + grid = FALSE) { + + # Check parameters + roiCRS <- sf::st_crs(roi) + + # if (class(roi)[1] == "sf") { + # roi <- sf::as_Spatial(roi) + # } + # if (class(roi)[1] != "SpatialPolygonsDataFrame") + # stop("ROI is not a valid polygon layer") + # if (length(grep("units=m", as.character(roi@proj4string@projargs))) == 0) + # stop("ROI is not in a metric projection") + # if (methods::is(flight.params)[1] != "Flight Parameters") + # stop("Flight parameters is not an instance returned from flight.parameters()") + if (!is.logical(grid)) { + stop("grid has to be TRUE or FALSE") + } + + # Parameters calculated + flight.speed.kmh = flight.params@flight.speed.kmh + flightSpeedMs = flight.speed.kmh / 3.6 + height = flight.params@height + groundHeight = flight.params@ground.height + groundHeightOverlap = groundHeight * flight.params@front.overlap + flightLineDistance = flight.params@flight.line.distance + vertices <- sf::st_coordinates(roi)[,1:2] + + # Get bounding box parameters + if (flight.lines.angle != -1) { + minBbox = flightplanning:::getBBoxAngle(vertices, flight.lines.angle) + } else { + # if angle not specified use minimum possible bounding box + minBbox = shotGroups::getMinBBox(vertices) + } + + if (grid == FALSE) { + width = minBbox$height + height = minBbox$width + alpha = minBbox$angle + } else { + width = minBbox$width + height = minBbox$height + alpha = minBbox$angle-90 + } + + rads = alpha*pi/180 + centroid = apply(minBbox$pts, 2, mean) + + # Calculate points offset from centroid + # based on angle and width/height offsets + # width offsets (between flightlines) + nLines = ceiling(width / flightLineDistance) + 1 + xWidths = (-nLines/2):(nLines/2) * flightLineDistance + xWidths = rep(xWidths, each=2) + + # heights offset (one for upper half + # one for lower half) + heightDistance = groundHeight-groundHeightOverlap + heightAdjusted = height + 2*heightDistance + # Put offset to avoid intersection issues + heightAdjusted = heightAdjusted + heightDistance*2 + heightMHalf = -heightAdjusted/2 + heightPHalf = heightAdjusted/2 + yHeights = c(heightMHalf, heightPHalf) + + + # Switch position of the first point + if (starting.point == 2) { + yHeights = c(heightPHalf, heightMHalf) + } else if (starting.point == 3) { + xWidths = rev(xWidths) + yHeights = c(heightPHalf, heightMHalf) + } else if (starting.point == 4) { + xWidths = rev(xWidths) + } + + # Interleave one upper, two bottom + # two upper, two bottom... until end + yHeights = c(rep(c(yHeights, rev(yHeights)), nLines/2+1)) + yHeights = yHeights[1:length(xWidths)] + + + # Calculate translated x and y from + # angles and offsets from centroid + xys = data.frame( + x = -xWidths * sin(rads) + + yHeights * cos(rads), + y = xWidths * cos(rads) + + yHeights * sin(rads)) + + # Initial waypoints to intersect waypoints + waypoints = xys + rep(centroid, each=nrow(xys)) + + ################################################# + # Intersect each flight line from bounding box + # to match actual ROI + ################################################# + # For some reason gIntersection with MULTILINESTRING + # will return linestrings in inconsistent order + # though it will be done in a for loop + + # --------------------------------------------------------------------------------------------- + lines <- do.call( + sf::st_sfc, + lapply( + 1:(nrow(waypoints)-1), + function(i) { + sf::st_linestring( + matrix( + c(as.numeric(waypoints[i, ]), as.numeric(waypoints[i + 1, ])), + ncol = 2, byrow = TRUE + ) + ) + } + ) + ) + + lines <- lines |> + sf::st_as_sf(crs = roiCRS) + lines$ID <- seq.int(nrow(lines)) + + # --------------------------------------------------------------------------------------------- + inter <- suppressWarnings(sf::st_intersection( + sf::st_buffer(sf::st_as_sf(roi), flightLineDistance), + lines) + ) |> subset(select = c("ID", "geometry")) |> + sf::st_cast(to = "LINESTRING") + inter + # --------------------------------------------------------------------------------------------- + nLines <- length(inter) + # gflightLines <- + waypoints <- sf::st_coordinates(sf::st_as_sf(inter))[,1:2] + + # --------------------------------------------------------------------------------------------- + + # Calculate curves points to allow smooth curves + curvedPoints = flightplanning:::outerCurvePoints(waypoints = waypoints, + angle = alpha, + flightLineDistance = flightLineDistance) + + # Adjust curve points position to avoid acute angles + adjustedCurves = flightplanning:::adjustAcuteAngles(xy = curvedPoints, + angle = alpha, + minAngle = 80) + + # Concatenate regular waypoints with curve waypoints + wptsMatrix = as.data.frame(matrix(nrow=nrow(waypoints)+nrow(adjustedCurves),ncol=4)) + colnames(wptsMatrix) = colnames=c("x", "y", "isCurve", "takePhoto") + mat_pos = 1 + for (i in seq_len(nrow(waypoints))) { + curve = as.vector(adjustedCurves[i, ]) + hasCurve = !anyNA(curve) + if (hasCurve) { + if (curve$before) { + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) + mat_pos = mat_pos + 1 + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + } else { + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) + mat_pos = mat_pos + 1 + } + } else { + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + } + } + waypoints = wptsMatrix + + # Break if distance greater than the maxWaypointDistance + waypointsXY = waypoints[, c("x", "y")] + distances = sqrt(diff(waypoints$x)**2 + diff(waypoints$y)**2) + breakIdx = distances > max.waypoints.distance + + newSize = nrow(waypoints) + sum(breakIdx) + if (newSize != nrow(waypoints)) { + midpoints = (waypointsXY[breakIdx,] + waypointsXY[-1,][breakIdx,])/2 + waypoints2 = data.frame(x = numeric(newSize), + y = numeric(newSize), + isCurve = FALSE, + takePhoto = TRUE) + + pos = seq_along(breakIdx)[breakIdx] + idx = pos + order(pos) + waypoints2[idx,1:2] = midpoints + waypoints2[-idx,] = waypoints + waypoints = waypoints2 + } +waypoints + + # --------------------------------------------------------------------------------------------- + t <- waypoints |> + sf::st_as_sf(coords = c("x", "y"), crs = roiCRS) |> + sf::st_transform(crs = "EPSG:4326") + lngs <- as.numeric(sf::st_coordinates(t)[,1]) + lats <- as.numeric(sf::st_coordinates(t)[,2]) + # --------------------------------------------------------------------------------------------- + + graphics::plot(waypoints[,1:2]) + graphics::polygon(sf::st_coordinates(roi)) + + # Calculate heading + nWaypoints = nrow(waypoints) + latDiff = lats[-1]-lats[-nWaypoints] + lngDiff = lngs[-1]-lngs[-nWaypoints] + headDegree = atan(latDiff/lngDiff)/pi*180 + finalHeading = 270-headDegree + finalHeading[lngDiff > 0] = 90-headDegree[lngDiff > 0] + + # Set parameters of the flight in the CSV + dfLitchi = read.csv(system.file("extdata/litchi.csv", package = "flightplanning")) + dfLitchi = dfLitchi[rep(1, length(lats)),] + dfLitchi$latitude = lats + dfLitchi$longitude = lngs + dfLitchi$altitude.m. = flight.params@height + dfLitchi$speed.m.s. = flightSpeedMs + dfLitchi$heading.deg. = c(finalHeading, 90) + dfLitchi$curvesize.m. = 0 + dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 + dfLitchi$photo_distinterval = flight.params@ground.height + dfLitchi$gimbalpitchangle = gimbal.pitch.angle + + + # Split the flight if is too long + dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) + distAcum = c(0,cumsum(dists)) + flightTime = distAcum / (flightSpeedMs*0.75) / 60 + finalSize = nrow(dfLitchi) + totalFlightTime = flightTime[finalSize] + dfLitchi$split = 1 + if (totalFlightTime > max.flight.time) { + indexes = seq_len(finalSize) + nBreaks = ceiling(totalFlightTime/max.flight.time) + breaks = seq(0, flightTime[finalSize], length.out = nBreaks+1)[c(-1, -nBreaks-1)] + endWaypointsIndex = indexes[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] + endWaypoints = flightTime[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] + selected = sapply(breaks, function(x) which.min(abs(endWaypoints-x))) + waypointsBreak = endWaypointsIndex[indexes[selected]] + + + dfLitchi$split = rep(1:nBreaks, diff(c(0, waypointsBreak, finalSize))) + splits = split.data.frame(dfLitchi, f = dfLitchi$split) + message("Your flight was splitted in ", length(splits), " splits, +because the total time would be ", round(totalFlightTime, 2), " minutes.") + message("They were saved as:") + first = substr(output, 1, nchar(output)-4) + second = substr(output, nchar(output)-3, nchar(output)) + for (dataSplit in splits) { + i = dataSplit[1, ]$split + output2 = paste0(first, i, second) + write.csv(dataSplit[,-ncol(dataSplit)], output2, row.names = FALSE) + message(output2) + } + output2 = paste0(first, "_entire", second) + write.csv(dfLitchi, output2, row.names = FALSE) + message("The entire flight plan was saved as:") + message(output2) + } else { + write.csv(dfLitchi, output, row.names = FALSE) + } + + colors = grDevices::rainbow(length(unique(dfLitchi$split))) + for (i in unique(dfLitchi$split)) + { + graphics::lines(waypoints[dfLitchi$split == i,1:2], lty=2, col=colors[as.integer(i)]) + } + graphics::text(waypoints[,1], waypoints[,2], seq_along(waypoints[,1]), pos=3) + + + message("#####################") + message("## Flight settings ## ") + message("#####################") + message("Min shutter speed: ", appendLF = FALSE) + message(flight.params@minimum.shutter.speed) + message("Photo interval: ", appendLF = FALSE) + message(flight.params@photo.interval, appendLF = FALSE) + message(" s") + message("Flight speed: ", appendLF = FALSE) + message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) + message(" km/h") + message("Flight lines angle: ", appendLF = FALSE) + message(round(alpha, 4)) + message('Total flight time: ', appendLF = FALSE) + message(round(totalFlightTime, 4)) + + return (waypoints) + +} + +rename_geometry <- function(df, name){ + current = attr(df, "sf_column") + names(df)[names(df) == current] = name + sf::st_geometry(df) = name + df +} + +f <- list.files(path = "mytest", pattern = ".csv", full.names = TRUE) +unlink(f) diff --git a/mytest/uav.R b/mytest/uav.R new file mode 100644 index 0000000..99c4697 --- /dev/null +++ b/mytest/uav.R @@ -0,0 +1,412 @@ +Wysokosc <- 100 +output = ("mytest/lot.csv") +roi = sf::st_read("mytest/lasek.gpkg") + +# if( nrow(roi) > 1) { +# for (i in seq_len(nrow(roi))) { +# output <- paste0("mytest/lot_", i, ".csv") +# flightplanning::litchi.plan(roi[i,], +# output, +# params, +# flight.lines.angle = -1, +# max.waypoints.distance = 4000, +# max.flight.time = 16, +# grid = FALSE +# ) +# } +# } + +if(nrow(roi) > 1) { + roi <- sf::st_union(roi) |> + sf::st_as_sf() +} + +params = flightplanning::flight.parameters(height=Wysokosc, + flight.speed.kmh=24, + side.overlap = 0.8, + front.overlap = 0.8) + +# Create the csv plan +# flightplanning::litchi.plan(roi, +# output, +# params, +# flight.lines.angle = -1, +# max.waypoints.distance = 4000, +# max.flight.time = 16, +# grid = FALSE +# ) + +flight.params <- params +gimbal.pitch.angle = -90 +flight.lines.angle = -1 +max.waypoints.distance = 2000 +max.flight.time = 15 +starting.point = 1 +grid = FALSE + +litchi.plan = function(roi, output, + flight.params, gimbal.pitch.angle = -90, + flight.lines.angle = -1, max.waypoints.distance = 2000, + max.flight.time = 15, starting.point = 1, grid = FALSE) { + # Check parameters + roiCRS <- sf::st_crs(roi) + + if (class(roi)[1] == "sf") { + roi <- sf::as_Spatial(roi) + } + if (class(roi)[1] != "SpatialPolygonsDataFrame") + stop("ROI is not a valid polygon layer") + if (length(grep("units=m", as.character(roi@proj4string@projargs))) == 0) + stop("ROI is not in a metric projection") + if (methods::is(flight.params)[1] != "Flight Parameters") + stop("Flight parameters is not an instance returned from flight.parameters()") + if (!is.logical(grid)) { + stop("parallel has to be TRUE or FALSE") + } + + # Parameters calculated + flight.speed.kmh = flight.params@flight.speed.kmh + flightSpeedMs = flight.speed.kmh / 3.6 + height = flight.params@height + groundHeight = flight.params@ground.height + groundHeightOverlap = groundHeight * flight.params@front.overlap + flightLineDistance = flight.params@flight.line.distance + vertices = roi@polygons[[1]]@Polygons[[1]]@coords + + # Get bounding box parameters + if (flight.lines.angle != -1) { + minBbox = flightplanning:::getBBoxAngle(vertices, flight.lines.angle) + } else { + # if angle not specified use minimum possible bounding box + minBbox = shotGroups::getMinBBox(vertices) + } + + if (grid == FALSE) { + width = minBbox$height + height = minBbox$width + alpha = minBbox$angle + } else { + width = minBbox$width + height = minBbox$height + alpha = minBbox$angle-90 + } + + rads = alpha*pi/180 + centroid = apply(minBbox$pts, 2, mean) + + # Calculate points offset from centroid + # based on angle and width/height offsets + # width offsets (between flightlines) + nLines = ceiling(width / flightLineDistance) + 1 + xWidths = (-nLines/2):(nLines/2) * flightLineDistance + xWidths = rep(xWidths, each=2) + + # heights offset (one for upper half + # one for lower half) + heightDistance = groundHeight-groundHeightOverlap + heightAdjusted = height + 2*heightDistance + # Put offset to avoid intersection issues + heightAdjusted = heightAdjusted + heightDistance*2 + heightMHalf = -heightAdjusted/2 + heightPHalf = heightAdjusted/2 + yHeights = c(heightMHalf, heightPHalf) + + + # Switch position of the first point + if (starting.point == 2) { + yHeights = c(heightPHalf, heightMHalf) + } else if (starting.point == 3) { + xWidths = rev(xWidths) + yHeights = c(heightPHalf, heightMHalf) + } else if (starting.point == 4) { + xWidths = rev(xWidths) + } + + # Interleave one upper, two bottom + # two upper, two bottom... until end + yHeights = c(rep(c(yHeights, rev(yHeights)), nLines/2+1)) + yHeights = yHeights[1:length(xWidths)] + + + # Calculate translated x and y from + # angles and offsets from centroid + xys = data.frame( + x = -xWidths * sin(rads) + + yHeights * cos(rads), + y = xWidths * cos(rads) + + yHeights * sin(rads)) + + # Initial waypoints to intersect waypoints + waypoints = xys + rep(centroid, each=nrow(xys)) + + ################################################# + # Intersect each flight line from bounding box + # to match actual ROI + ################################################# + # For some reason gIntersection with MULTILINESTRING + # will return linestrings in inconsistent order + # though it will be done in a for loop + + # + # + # wktLines = paste(apply(waypoints, 1, paste, collapse=" "), collapse=", ") + # wktLines = paste("LINESTRING(", wktLines,")") + # gLines = rgeos::readWKT(wktLines, p4s = roi@proj4string) + # inter = rgeos::gIntersection(rgeos::gBuffer(roi, width = flightLineDistance), gLines) + # nLines = length(inter@lines[[1]]@Lines) + + # flightLines = t(sapply(inter@lines[[1]]@Lines, function(x) x@coords)) + + # RSB + glist <- vector(mode="list", length=nrow(waypoints)-1) + for (i in seq_along(glist)) glist[[i]] <- sp::Lines(list(sp::Line(waypoints[c(i, (i+1)),])), ID=as.character(i)) + gLines <- sp::SpatialLines(glist, proj4string=slot(roi, "proj4string")) + +# --------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------- + lines <- do.call( + sf::st_sfc, + lapply( + 1:(nrow(waypoints)-1), + function(i) { + sf::st_linestring( + matrix( + c(as.numeric(waypoints[i, ]), as.numeric(waypoints[i + 1, ])), + ncol = 2, byrow = TRUE + ) + ) + } + ) + ) + + lines <- lines |> + sf::st_as_sf(crs = roiCRS) + lines$ID <- seq.int(nrow(lines)) +# rename_geometry(lines, "geom") + # class(lines) + # sp::plot(roi) + # terra::plot(lines, add = TRUE) + mgLines <- lines |> + sf::as_Spatial() + +sp::plot(gLines) +sp::plot(mgLines, add = TRUE, col = "yellow") +class(gLines) +# --------------------------------------------------------------------------------------------- + inter = rgeos::gIntersection(rgeos::gBuffer(roi, width = flightLineDistance), gLines, byid=TRUE) +# --------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------- + ginter <- sf::st_intersection( + sf::st_buffer(sf::st_as_sf(roi), flightLineDistance), + lines) + # myinter <- ginter + # ginter <- ginter |> + # sf::as_Spatial() + # class(ginter) + # terra::plot(myinter, add = TRUE, col = "yellow") + +# --------------------------------------------------------------------------------------------- + + nLines <- length(inter) + flightLines <- t(sapply(slot(inter, "lines"), function(x) slot(slot(x, "Lines")[[1]], "coords"))) + # RSB + flightLines = flightLines[,c(1,3,2,4)] +# flightLines + +# --------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------- + nLines <- length(ginter) + # gflightLines <- + a <- sf::st_coordinates(ginter)[,1:2] + # to poniżej nie potrzebne gdyż waypoints == a + # gflightLines = matrix(nrow = mnLines, ncol = 4) + # gflightLines[,1] <- a[seq(1,length(a[,1]),2),1] + # gflightLines[,2] <- a[seq(1,length(a[,1]),2),2] + # gflightLines[,3] <- a[seq(2,length(a[,1]),2),1] + # gflightLines[,4] <- a[seq(2,length(a[,1]),2),2] + # flightLines <- gflightLines +# --------------------------------------------------------------------------------------------- + + waypoints = matrix(nrow=nLines * 2, ncol=2) + waypoints[seq(1, nLines*2, 2),] = flightLines[, 1:2] + waypoints[seq(2, nLines*2, 2),] = flightLines[, 3:4] +waypoints +# --------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------- + +waypoints <- a +# --------------------------------------------------------------------------------------------- + + + # Calculate curves points to allow smooth curves + curvedPoints = flightplanning:::outerCurvePoints(waypoints = waypoints, + angle = alpha, + flightLineDistance = flightLineDistance) + + # Adjust curve points position to avoid acute angles + adjustedCurves = flightplanning:::adjustAcuteAngles(xy = curvedPoints, + angle = alpha, + minAngle = 80) + + # Concatenate regular waypoints with curve waypoints + wgs84 = "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0" + wptsMatrix = as.data.frame(matrix(nrow=nrow(waypoints)+nrow(adjustedCurves),ncol=4)) + colnames(wptsMatrix) = colnames=c("x", "y", "isCurve", "takePhoto") + mat_pos = 1 + for (i in seq_len(nrow(waypoints))) { + curve = as.vector(adjustedCurves[as.character(i),]) + hasCurve = !anyNA(curve) + if (hasCurve) { + if (curve$before) { + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) + mat_pos = mat_pos + 1 + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + } else { + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) + mat_pos = mat_pos + 1 + } + } else { + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + } + } + waypoints = wptsMatrix + + # Break if distance greater than the maxWaypointDistance + waypointsXY = waypoints[, c("x", "y")] + distances = sqrt(diff(waypoints$x)**2 + diff(waypoints$y)**2) + breakIdx = distances > max.waypoints.distance + + newSize = nrow(waypoints) + sum(breakIdx) + if (newSize != nrow(waypoints)) { + midpoints = (waypointsXY[breakIdx,] + waypointsXY[-1,][breakIdx,])/2 + waypoints2 = data.frame(x = numeric(newSize), + y = numeric(newSize), + isCurve = FALSE, + takePhoto = TRUE) + + pos = seq_along(breakIdx)[breakIdx] + idx = pos + order(pos) + waypoints2[idx,1:2] = midpoints + waypoints2[-idx,] = waypoints + waypoints = waypoints2 + } + + +# --------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------- + t <- waypoints |> + sf::st_as_sf(coords = c("x", "y"), crs = roiCRS) |> + sf::st_transform(crs = "EPSG:4326") + lngs <- as.numeric(sf::st_coordinates(t)[,1]) + lats <- as.numeric(sf::st_coordinates(t)[,2]) +# --------------------------------------------------------------------------------------------- + + + # Transform to WGS84 latitude and longitude + transform = rgdal::rawTransform(roi@proj4string@projargs, wgs84, n=nrow(waypoints), x=waypoints[,1], y=waypoints[,2]) + lats = transform[[2]] + lngs = transform[[1]] + graphics::plot(waypoints[,1:2]) + graphics::polygon(roi@polygons[[1]]@Polygons[[1]]@coords) + + + # Calculate heading + nWaypoints = nrow(waypoints) + latDiff = lats[-1]-lats[-nWaypoints] + lngDiff = lngs[-1]-lngs[-nWaypoints] + headDegree = atan(latDiff/lngDiff)/pi*180 + finalHeading = 270-headDegree + finalHeading[lngDiff > 0] = 90-headDegree[lngDiff > 0] + + # Set parameters of the flight in the CSV + dfLitchi = read.csv(system.file("extdata/litchi.csv", package = "flightplanning")) + dfLitchi = dfLitchi[rep(1, length(lats)),] + dfLitchi$latitude = lats + dfLitchi$longitude = lngs + dfLitchi$altitude.m. = flight.params@height + dfLitchi$speed.m.s. = flightSpeedMs + dfLitchi$heading.deg. = c(finalHeading, 90) + dfLitchi$curvesize.m. = 0 + dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 + dfLitchi$photo_distinterval = flight.params@ground.height + dfLitchi$gimbalpitchangle = gimbal.pitch.angle + + + # Split the flight if is too long + dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) + distAcum = c(0,cumsum(dists)) + flightTime = distAcum / (flightSpeedMs*0.75) / 60 + finalSize = nrow(dfLitchi) + totalFlightTime = flightTime[finalSize] + dfLitchi$split = 1 + if (totalFlightTime > max.flight.time) { + indexes = seq_len(finalSize) + nBreaks = ceiling(totalFlightTime/max.flight.time) + breaks = seq(0, flightTime[finalSize], length.out = nBreaks+1)[c(-1, -nBreaks-1)] + endWaypointsIndex = indexes[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] + endWaypoints = flightTime[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] + selected = sapply(breaks, function(x) which.min(abs(endWaypoints-x))) + waypointsBreak = endWaypointsIndex[indexes[selected]] + + + dfLitchi$split = rep(1:nBreaks, diff(c(0, waypointsBreak, finalSize))) + splits = split.data.frame(dfLitchi, f = dfLitchi$split) + message("Your flight was splitted in ", length(splits), " splits, +because the total time would be ", round(totalFlightTime, 2), " minutes.") + message("They were saved as:") + first = substr(output, 1, nchar(output)-4) + second = substr(output, nchar(output)-3, nchar(output)) + for (dataSplit in splits) { + i = dataSplit[1, ]$split + output2 = paste0(first, i, second) + write.csv(dataSplit[,-ncol(dataSplit)], output2, row.names = FALSE) + message(output2) + } + output2 = paste0(first, "_entire", second) + write.csv(dfLitchi, output2, row.names = FALSE) + message("The entire flight plan was saved as:") + message(output2) + } else { + write.csv(dfLitchi, output, row.names = FALSE) + } + + colors = grDevices::rainbow(length(unique(dfLitchi$split))) + for (i in unique(dfLitchi$split)) + { + graphics::lines(waypoints[dfLitchi$split == i,1:2], lty=2, col=colors[as.integer(i)]) + } + graphics::text(waypoints[,1], waypoints[,2], seq_along(waypoints[,1]), pos=3) + + + message("#####################") + message("## Flight settings ## ") + message("#####################") + message("Min shutter speed: ", appendLF = FALSE) + message(flight.params@minimum.shutter.speed) + message("Photo interval: ", appendLF = FALSE) + message(flight.params@photo.interval, appendLF = FALSE) + message(" s") + message("Flight speed: ", appendLF = FALSE) + message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) + message(" km/h") + message("Flight lines angle: ", appendLF = FALSE) + message(round(alpha, 4)) + message('Total flight time: ', appendLF = FALSE) + message(round(totalFlightTime, 4)) + + return (waypoints) +} + +rename_geometry <- function(df, name){ + current = attr(df, "sf_column") + names(df)[names(df) == current] = name + sf::st_geometry(df) = name + df +} + +f <- list.files(path = "mytest", pattern = ".csv", full.names = TRUE) +unlink(f) diff --git a/mytest/uav.gpkg b/mytest/uav.gpkg new file mode 100644 index 0000000000000000000000000000000000000000..bd1a16881fec585cee583f54dae56e7d5f610a7d GIT binary patch literal 98304 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 literal 0 HcmV?d00001 diff --git a/mytest/uav_test.R b/mytest/uav_test.R new file mode 100644 index 0000000..4c7d248 --- /dev/null +++ b/mytest/uav_test.R @@ -0,0 +1,50 @@ +library(flightplanning) +f <- "mytest/lasek.gpkg" +f <- "/home/sapi/projekty/flightplanning-R/mytest/lasek.gpkg" +roi <- sf::st_read(f) + +output <- "mytest/fly.csv" +output <- "/home/sapi/projekty/flightplanning-R/mytest/fly.csv" + +params <- flight.parameters( + height = 120, + focal.length35 = 24, + flight.speed.kmh = 24, + side.overlap = 0.8, + front.overlap = 0.8 +) + +litchi_sf(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = FALSE +) + +if(nrow(roi) > 1) { + roi <- sf::st_union(roi) +} +litchi_sf(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = FALSE +) + + + +# Create the csv plan +flightplanning::litchi.plan(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = FALSE) diff --git a/tests/testthat/test.R b/tests/testthat/test.R index b8bf5ba..f9ce927 100644 --- a/tests/testthat/test.R +++ b/tests/testthat/test.R @@ -147,9 +147,7 @@ test_that("Shutter speed calculation is correct", { test_that("Litchi plan outputs the csv file", { exampleBoundary = sf::st_read( - system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> - sf::as_Spatial() - + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) outPath = tempfile(fileext=".csv") params = flight.parameters( @@ -159,7 +157,7 @@ test_that("Litchi plan outputs the csv file", { flight.speed.kmh = 54 ) - litchi.plan(exampleBoundary, + litchi_sf(exampleBoundary, outPath, params) title("Defaults") @@ -170,8 +168,7 @@ test_that("Litchi plan outputs the csv file", { test_that("Different starting points are working", { exampleBoundary = sf::st_read( - system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> - sf::as_Spatial() + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) outPath = tempfile(fileext=".csv") params = flight.parameters( @@ -181,17 +178,17 @@ test_that("Different starting points are working", { flight.speed.kmh = 54 ) - litchi.plan(exampleBoundary, + litchi_sf(exampleBoundary, outPath, params, starting.point = 2) title("Starting point 2") - litchi.plan(exampleBoundary, + litchi_sf(exampleBoundary, outPath, params, starting.point = 3) title("Starting point 3") - litchi.plan(exampleBoundary, + litchi_sf(exampleBoundary, outPath, params, starting.point = 4) @@ -202,8 +199,7 @@ test_that("Different starting points are working", { test_that("Different flight line angles are working", { exampleBoundary = sf::st_read( - system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> - sf::as_Spatial() + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) outPath = tempfile(fileext=".csv") params = flight.parameters( @@ -213,17 +209,17 @@ test_that("Different flight line angles are working", { flight.speed.kmh = 54 ) - litchi.plan(exampleBoundary, + litchi_sf(exampleBoundary, outPath, params, flight.lines.angle = 45) title("45 degrees") - litchi.plan(exampleBoundary, + litchi_sf(exampleBoundary, outPath, params, flight.lines.angle = 90) title("90 degrees") - litchi.plan(exampleBoundary, + litchi_sf(exampleBoundary, outPath, params, flight.lines.angle = 135) @@ -234,37 +230,37 @@ test_that("Different flight line angles are working", { test_that("Did not provide legal ROI", { outPath = tempfile(fileext=".csv") - expect_error( litchi.plan(NA, outPath, NA) ) + expect_error( litchi_sf(NA, outPath, NA) ) }) test_that("ROI is not in a metric projection", { outPath = tempfile(fileext=".csv") exampleBoundary = sf::st_read( - system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> - sf::as_Spatial() + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) + roi = exampleBoundary |> sf::st_as_sf() |> - sf::st_transform(crs = "EPSG:4326") |> - sf::as_Spatial() - expect_error( litchi.plan(roi, outPath, NA) ) + sf::st_transform(crs = "EPSG:4326") + + expect_error( litchi_sf(roi, outPath, NA) ) }) test_that("Did not provide Flight Parameters", { exampleBoundary = sf::st_read( - system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> - sf::as_Spatial() + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) + outPath = tempfile(fileext=".csv") - expect_error( litchi.plan(exampleBoundary, outPath, NA) ) + expect_error( litchi_sf(exampleBoundary, outPath, NA) ) }) test_that("Break waypoints too far", { outPath = tempfile(fileext=".csv") exampleBoundary = sf::st_read( - system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> - sf::as_Spatial() + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) + params = flight.parameters( gsd = 4, side.overlap = 0, @@ -272,7 +268,7 @@ test_that("Break waypoints too far", { flight.speed.kmh = 54 ) - litchi.plan(exampleBoundary, outPath, params, + litchi_sf(exampleBoundary, outPath, params, max.waypoints.distance = 1000) title("Break waypoints farther than 1000 meters") @@ -283,8 +279,8 @@ test_that("Break waypoints too far", { test_that("Break flight if exceeds max flight time", { outPath = tempfile(fileext=".csv") exampleBoundary = sf::st_read( - system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> - sf::as_Spatial() + system.file("extdata", "exampleBoundary.shp", package="flightplanning")) + params = flight.parameters( gsd = 4, side.overlap = 0, @@ -292,7 +288,7 @@ test_that("Break flight if exceeds max flight time", { flight.speed.kmh = 54 ) - litchi.plan(exampleBoundary, outPath, params, + litchi_sf(exampleBoundary, outPath, params, max.flight.time = 10) title("Break into multiple flights") expect_equal(length(Sys.glob(paste0(tools::file_path_sans_ext(outPath), "*.csv"))), 3) From 02fb79ce250776d72083f0ecab135ce8ab509bfa Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Sun, 20 Nov 2022 21:29:30 +0100 Subject: [PATCH 13/35] del csv --- mytest/fly.csv | 45 ------------------- mytest/fly1.csv | 47 -------------------- mytest/fly2.csv | 55 ----------------------- mytest/fly_entire.csv | 101 ------------------------------------------ 4 files changed, 248 deletions(-) delete mode 100644 mytest/fly.csv delete mode 100644 mytest/fly1.csv delete mode 100644 mytest/fly2.csv delete mode 100644 mytest/fly_entire.csv diff --git a/mytest/fly.csv b/mytest/fly.csv deleted file mode 100644 index c56add5..0000000 --- a/mytest/fly.csv +++ /dev/null @@ -1,45 +0,0 @@ -"latitude","longitude","altitude.m.","heading.deg.","curvesize.m.","rotationdir","gimbalmode","gimbalpitchangle","actiontype1","actionparam1","actiontype2","actionparam2","actiontype3","actionparam3","actiontype4","actionparam4","actiontype5","actionparam5","actiontype6","actionparam6","actiontype7","actionparam7","actiontype8","actionparam8","actiontype9","actionparam9","actiontype10","actionparam10","actiontype11","actionparam11","actiontype12","actionparam12","actiontype13","actionparam13","actiontype14","actionparam14","actiontype15","actionparam15","altitudemode","speed.m.s.","poi_latitude","poi_longitude","poi_altitude.m.","poi_altitudemode","photo_timeinterval","photo_distinterval","split" -51.2919683460086,16.7790764766696,120,335.344305730144,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2922674513242,16.7789391837187,120,335.343070014653,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.294387870021,16.7779658305699,120,335.340903877391,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2965082788128,16.7769923849096,120,335.33961721376,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.296906602515,16.7768095100576,120,259.770773559568,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2968205096737,16.7763324226227,120,155.33907903129,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2965214080054,16.7764697469346,120,155.34036570435,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2943018148904,16.777488751769,120,155.342633211142,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2920822109213,16.7785076552323,120,155.343919602709,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2917831061306,16.7786449503826,120,259.770674256399,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2916970213075,16.778167912112,120,335.343360675317,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.292136184409,16.7779663221765,120,335.342092854172,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2941793131558,16.7770284058784,120,335.340005593834,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2962224327054,16.776090403686,120,335.338809060882,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2965215339236,16.7759530778745,120,259.770255586602,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2964354376542,16.7754759961221,120,155.338510690194,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2957661890451,16.7757832712683,120,155.339762243108,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2939855385774,16.7766007822905,120,155.341581385563,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2922048811234,16.7774182280686,120,155.34264364643,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2919057777075,16.7775555306524,120,259.77013988513,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2918196884549,16.7770784932932,120,335.342077200383,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2922735801454,16.7768701301534,120,335.340130469702,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2956308627014,16.7753288007202,120,335.338262634681,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2959299630151,16.7751914718701,120,259.769744308496,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2958438637255,16.7747143977402,120,155.337896001058,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2953082839613,16.7749603078612,120,155.339705081646,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2923025575309,16.7763402647906,120,155.341393212261,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2920034554735,16.776477574654,120,259.769606314731,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2919173618406,16.7760005384413,120,335.340875618652,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.292275624933,16.775836065389,120,335.33936163122,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2948812315318,16.7746397882806,120,335.337877759602,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2951803310453,16.7745024573652,120,259.769238157631,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2950942290476,16.7740253923424,120,155.337597778397,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2943890858257,16.7743491615487,120,155.339052412867,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.292246793893,16.7753327372255,120,155.340299529448,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.291947693093,16.7754700534244,120,264.277907440532,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2918982086894,16.774976210477,120,335.339774760004,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2921973088749,16.7748388912434,120,335.338738079298,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2939276241643,16.7740444520593,120,335.337701344326,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.294226723012,16.7739071203345,120,259.768738630692,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2941406187092,16.7734300663346,120,155.337334129579,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2936062829155,16.7736754116343,120,155.338302835104,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2922444068319,16.7743007019532,120,155.33915132502,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2919453073246,16.7744380248159,120,90,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 diff --git a/mytest/fly1.csv b/mytest/fly1.csv deleted file mode 100644 index a3e2cae..0000000 --- a/mytest/fly1.csv +++ /dev/null @@ -1,47 +0,0 @@ -"latitude","longitude","altitude.m.","heading.deg.","curvesize.m.","rotationdir","gimbalmode","gimbalpitchangle","actiontype1","actionparam1","actiontype2","actionparam2","actiontype3","actionparam3","actiontype4","actionparam4","actiontype5","actionparam5","actiontype6","actionparam6","actiontype7","actionparam7","actiontype8","actionparam8","actiontype9","actionparam9","actiontype10","actionparam10","actiontype11","actionparam11","actiontype12","actionparam12","actiontype13","actionparam13","actiontype14","actionparam14","actiontype15","actionparam15","altitudemode","speed.m.s.","poi_latitude","poi_longitude","poi_altitude.m.","poi_altitudemode","photo_timeinterval","photo_distinterval" -51.2918859381692,16.7792638927025,120,79.8226026966069,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.291971610885,16.7797411217558,120,79.8227816384004,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2920028660525,16.7799152278493,120,79.8230677782738,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2921585788247,16.7807826466846,120,335.457544871711,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.292457803283,16.7806460143841,120,259.823165375639,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2923721341817,16.7801687785165,120,259.822579539039,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2920749986892,16.7785136265937,120,259.821732812637,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2918188321372,16.7770868076956,120,335.453597157208,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2921180520156,16.7769501525706,120,79.8214623148865,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2922037341397,16.7774273794166,120,79.8223090463432,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2926713575421,16.7800321398158,120,79.8231557733601,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2927570272312,16.7805093784983,120,335.4566353831,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.293056250984,16.7803727407798,120,259.823146172011,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.292970580707,16.7798954992825,120,259.821973520838,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2922899602206,16.776104428113,120,259.820326427728,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2918941990096,16.7739003937032,120,335.450006397802,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2921934149075,16.773763717732,120,79.8198423758324,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2922791099608,16.7742409390171,120,79.8207314867395,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2927744889951,16.7769998673352,120,79.822247469864,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2932698036766,16.7797588569164,120,79.8231365691859,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2933554745414,16.7802361012285,120,335.456026331183,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2936546979034,16.7800994598445,120,259.823126967217,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2935690264507,16.7796222127174,120,259.822230427394,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2930688494466,16.776836143912,120,259.82069956577,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2925686068205,16.7740501375791,120,259.819803014126,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2924829109607,16.7735729136876,120,334.288106345404,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.292780903441,16.7734294233984,120,79.8197896544442,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2928665999163,16.7739066500781,120,79.8206871435404,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2933674573644,16.7766960770684,120,79.822219884751,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2938682490293,16.7794855666856,120,79.8231173645987,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2939539210699,16.7799628166276,120,335.455417256986,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2942531440411,16.7798261715779,120,259.823107758778,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2941674714125,16.7793489188209,120,259.822209341555,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2936660650782,16.7765560083517,120,259.820674721284,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2931645927994,16.7737631606639,120,259.819776289951,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2930788957085,16.7732859311959,120,334.293782069667,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2933768944397,16.7731424742594,120,79.8197629475071,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2934625921461,16.7736197065158,120,79.8206623142941,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2939646760181,16.7764159568664,120,79.8221988031833,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.294466693785,16.7792122701522,120,79.8230981580941,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2945523670015,16.7796895257243,120,335.455382859064,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2948515901835,16.7795528803611,120,259.823088556083,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2947659163791,16.779075621974,120,259.822188269372,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2942632895276,16.7762759189563,120,259.820649919111,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2937605964109,16.7734762790273,120,259.819749619906,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2936748980892,16.7729990439823,120,334.280336626995,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 diff --git a/mytest/fly2.csv b/mytest/fly2.csv deleted file mode 100644 index faecfa4..0000000 --- a/mytest/fly2.csv +++ /dev/null @@ -1,55 +0,0 @@ -"latitude","longitude","altitude.m.","heading.deg.","curvesize.m.","rotationdir","gimbalmode","gimbalpitchangle","actiontype1","actionparam1","actiontype2","actionparam2","actiontype3","actionparam3","actiontype4","actionparam4","actiontype5","actionparam5","actiontype6","actionparam6","actiontype7","actionparam7","actiontype8","actionparam8","actiontype9","actionparam9","actiontype10","actionparam10","actiontype11","actionparam11","actiontype12","actionparam12","actiontype13","actionparam13","actiontype14","actionparam14","actiontype15","actionparam15","altitudemode","speed.m.s.","poi_latitude","poi_longitude","poi_altitude.m.","poi_altitudemode","photo_timeinterval","photo_distinterval" -51.2939728828231,16.7728555076417,120,79.8197362346309,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2940585817605,16.7733327454749,120,79.8206374803493,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2945618934832,16.7761358270964,120,79.8221777210741,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2950651387778,16.7789389719627,120,79.8230789516217,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2951508131701,16.779416233165,120,335.454773740764,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2954500359612,16.7792795841359,120,259.823069349891,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.295364360981,16.7788023201184,120,259.82216717171,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2948604968636,16.7759957312961,120,259.82062503911,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.294356566155,16.7731892058748,120,259.819722849058,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2942708666018,16.7727119652533,120,335.447472191989,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2945700808179,16.7725752740545,120,79.8197515492192,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2946808287884,16.7731920051858,120,79.8214248224937,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2956635829888,16.778665666441,120,79.8230597481903,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2957492585569,16.7791429332737,120,335.454164598128,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2960484809571,16.7790062805784,120,259.823050143287,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.295962804801,16.7785290109305,120,259.821533720513,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2950575011417,16.7734866720519,120,259.820017274852,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2949718027152,16.7730094236078,120,335.447447163395,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2952710172436,16.7728727321083,120,79.8201256746835,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2954338502843,16.7737795475972,120,79.821642125784,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2962620264178,16.7783923535868,120,79.8230405402142,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2963477031618,16.77886962605,120,335.453555427462,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2966469251711,16.7787329696884,120,259.823030936418,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2965612478392,16.7782556944098,120,259.821748506218,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2958088746835,16.7740650539091,120,259.820466063626,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2957231785172,16.7735877965628,120,335.447709679127,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2960223937042,16.7734511064195,120,79.8205724380697,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2961838984207,16.7743505648026,120,79.8218548825766,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2968604657419,16.7781190148882,120,79.8230213204257,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2969461436619,16.778596292982,120,335.451567772658,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2972453638368,16.7784596249126,120,259.823011710385,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2971596853287,16.7779823440034,120,259.821961262636,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2965589214959,16.7746360802778,120,259.82091080495,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2964732275603,16.7741588140573,120,335.447964686588,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2967724433967,16.774022125227,120,79.82101718261,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2969339439092,16.7749216003349,120,79.8215722753866,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2971352039346,16.776042577994,120,79.8225067356959,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2975445826598,16.7783229485683,120,335.452062726937,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2978438035999,16.7781862832734,120,259.822992490688,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2977581239158,16.7777089967333,120,259.82247735627,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2975071907525,16.7763112227912,120,259.821962222233,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2974215035026,16.7758339434543,120,335.449349965291,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2977207213489,16.7756972624499,120,79.8220059768095,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.29784128424,16.7763688026083,120,79.8225211139331,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.298057342916,16.777572320348,120,79.8229828793043,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2981430231882,16.7780496097036,120,335.448258727057,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2984422403918,16.7779129221047,120,259.822973261424,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2983565595314,16.7774356299336,120,259.822564867824,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2981753776916,16.7764263831872,120,259.822156473261,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2980896908331,16.775949096727,120,335.449138371802,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.298388908767,16.7758124143469,120,79.8222690951576,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2985544717075,16.7767346309419,120,79.8225702846434,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2985857255846,16.7769087261549,120,79.8227492393285,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 -51.2986714086076,16.7773860196425,120,90,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704 diff --git a/mytest/fly_entire.csv b/mytest/fly_entire.csv deleted file mode 100644 index 06c15e1..0000000 --- a/mytest/fly_entire.csv +++ /dev/null @@ -1,101 +0,0 @@ -"latitude","longitude","altitude.m.","heading.deg.","curvesize.m.","rotationdir","gimbalmode","gimbalpitchangle","actiontype1","actionparam1","actiontype2","actionparam2","actiontype3","actionparam3","actiontype4","actionparam4","actiontype5","actionparam5","actiontype6","actionparam6","actiontype7","actionparam7","actiontype8","actionparam8","actiontype9","actionparam9","actiontype10","actionparam10","actiontype11","actionparam11","actiontype12","actionparam12","actiontype13","actionparam13","actiontype14","actionparam14","actiontype15","actionparam15","altitudemode","speed.m.s.","poi_latitude","poi_longitude","poi_altitude.m.","poi_altitudemode","photo_timeinterval","photo_distinterval","split" -51.2918859381692,16.7792638927025,120,79.8226026966069,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.291971610885,16.7797411217558,120,79.8227816384004,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2920028660525,16.7799152278493,120,79.8230677782738,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2921585788247,16.7807826466846,120,335.457544871711,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.292457803283,16.7806460143841,120,259.823165375639,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2923721341817,16.7801687785165,120,259.822579539039,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2920749986892,16.7785136265937,120,259.821732812637,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2918188321372,16.7770868076956,120,335.453597157208,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2921180520156,16.7769501525706,120,79.8214623148865,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2922037341397,16.7774273794166,120,79.8223090463432,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2926713575421,16.7800321398158,120,79.8231557733601,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2927570272312,16.7805093784983,120,335.4566353831,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.293056250984,16.7803727407798,120,259.823146172011,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.292970580707,16.7798954992825,120,259.821973520838,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2922899602206,16.776104428113,120,259.820326427728,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2918941990096,16.7739003937032,120,335.450006397802,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2921934149075,16.773763717732,120,79.8198423758324,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2922791099608,16.7742409390171,120,79.8207314867395,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2927744889951,16.7769998673352,120,79.822247469864,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2932698036766,16.7797588569164,120,79.8231365691859,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2933554745414,16.7802361012285,120,335.456026331183,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2936546979034,16.7800994598445,120,259.823126967217,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2935690264507,16.7796222127174,120,259.822230427394,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2930688494466,16.776836143912,120,259.82069956577,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2925686068205,16.7740501375791,120,259.819803014126,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2924829109607,16.7735729136876,120,334.288106345404,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.292780903441,16.7734294233984,120,79.8197896544442,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2928665999163,16.7739066500781,120,79.8206871435404,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2933674573644,16.7766960770684,120,79.822219884751,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2938682490293,16.7794855666856,120,79.8231173645987,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2939539210699,16.7799628166276,120,335.455417256986,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2942531440411,16.7798261715779,120,259.823107758778,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2941674714125,16.7793489188209,120,259.822209341555,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2936660650782,16.7765560083517,120,259.820674721284,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2931645927994,16.7737631606639,120,259.819776289951,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2930788957085,16.7732859311959,120,334.293782069667,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2933768944397,16.7731424742594,120,79.8197629475071,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2934625921461,16.7736197065158,120,79.8206623142941,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2939646760181,16.7764159568664,120,79.8221988031833,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.294466693785,16.7792122701522,120,79.8230981580941,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2945523670015,16.7796895257243,120,335.455382859064,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2948515901835,16.7795528803611,120,259.823088556083,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2947659163791,16.779075621974,120,259.822188269372,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2942632895276,16.7762759189563,120,259.820649919111,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2937605964109,16.7734762790273,120,259.819749619906,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2936748980892,16.7729990439823,120,334.280336626995,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,1 -51.2939728828231,16.7728555076417,120,79.8197362346309,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2940585817605,16.7733327454749,120,79.8206374803493,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2945618934832,16.7761358270964,120,79.8221777210741,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2950651387778,16.7789389719627,120,79.8230789516217,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2951508131701,16.779416233165,120,335.454773740764,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2954500359612,16.7792795841359,120,259.823069349891,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.295364360981,16.7788023201184,120,259.82216717171,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2948604968636,16.7759957312961,120,259.82062503911,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.294356566155,16.7731892058748,120,259.819722849058,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2942708666018,16.7727119652533,120,335.447472191989,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2945700808179,16.7725752740545,120,79.8197515492192,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2946808287884,16.7731920051858,120,79.8214248224937,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2956635829888,16.778665666441,120,79.8230597481903,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2957492585569,16.7791429332737,120,335.454164598128,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2960484809571,16.7790062805784,120,259.823050143287,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.295962804801,16.7785290109305,120,259.821533720513,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2950575011417,16.7734866720519,120,259.820017274852,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2949718027152,16.7730094236078,120,335.447447163395,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2952710172436,16.7728727321083,120,79.8201256746835,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2954338502843,16.7737795475972,120,79.821642125784,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2962620264178,16.7783923535868,120,79.8230405402142,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2963477031618,16.77886962605,120,335.453555427462,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2966469251711,16.7787329696884,120,259.823030936418,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2965612478392,16.7782556944098,120,259.821748506218,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2958088746835,16.7740650539091,120,259.820466063626,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2957231785172,16.7735877965628,120,335.447709679127,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2960223937042,16.7734511064195,120,79.8205724380697,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2961838984207,16.7743505648026,120,79.8218548825766,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2968604657419,16.7781190148882,120,79.8230213204257,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2969461436619,16.778596292982,120,335.451567772658,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2972453638368,16.7784596249126,120,259.823011710385,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2971596853287,16.7779823440034,120,259.821961262636,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2965589214959,16.7746360802778,120,259.82091080495,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2964732275603,16.7741588140573,120,335.447964686588,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2967724433967,16.774022125227,120,79.82101718261,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2969339439092,16.7749216003349,120,79.8215722753866,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2971352039346,16.776042577994,120,79.8225067356959,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2975445826598,16.7783229485683,120,335.452062726937,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2978438035999,16.7781862832734,120,259.822992490688,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2977581239158,16.7777089967333,120,259.82247735627,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2975071907525,16.7763112227912,120,259.821962222233,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2974215035026,16.7758339434543,120,335.449349965291,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2977207213489,16.7756972624499,120,79.8220059768095,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.29784128424,16.7763688026083,120,79.8225211139331,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.298057342916,16.777572320348,120,79.8229828793043,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2981430231882,16.7780496097036,120,335.448258727057,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2984422403918,16.7779129221047,120,259.822973261424,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2983565595314,16.7774356299336,120,259.822564867824,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2981753776916,16.7764263831872,120,259.822156473261,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2980896908331,16.775949096727,120,335.449138371802,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.298388908767,16.7758124143469,120,79.8222690951576,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2985544717075,16.7767346309419,120,79.8225702846434,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2985857255846,16.7769087261549,120,79.8227492393285,0,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 -51.2986714086076,16.7773860196425,120,90,17.3066461222271,0,0,-90,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,-1,0,0,6.48999229583518,0,0,0,0,0,129.799845916704,2 From 2abd7ca8aff6dc99e1e99f6ef7bae2887d4dad77 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Sun, 20 Nov 2022 21:54:56 +0100 Subject: [PATCH 14/35] st_geometry_type(roi) test --- R/litchi_sf.R | 6 ++++-- mytest/uav_test.R | 22 +++++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/R/litchi_sf.R b/R/litchi_sf.R index 548d7ac..342aca3 100644 --- a/R/litchi_sf.R +++ b/R/litchi_sf.R @@ -76,8 +76,9 @@ litchi_sf = function(roi, sf::st_as_sf() } - roiCRS <- sf::st_crs(roi) - + if(sf::st_geometry_type(roi)[[1]] %in% c("POLYGON", "MULTIPOLYGON")) { + stop("ROI is neither POLYGON nor MULTIPOLYGON") + } if (!grepl("LENGTHUNIT[\"metre\",1]", sf::st_crs(roi)[2], fixed = TRUE)) stop("ROI is not in a metric projection") if (methods::is(flight.params)[1] != "Flight Parameters") @@ -94,6 +95,7 @@ litchi_sf = function(roi, groundHeightOverlap = groundHeight * flight.params@front.overlap flightLineDistance = flight.params@flight.line.distance vertices <- sf::st_coordinates(roi)[,1:2] + roiCRS <- sf::st_crs(roi) # Get bounding box parameters if (flight.lines.angle != -1) { diff --git a/mytest/uav_test.R b/mytest/uav_test.R index 4c7d248..8ecbcad 100644 --- a/mytest/uav_test.R +++ b/mytest/uav_test.R @@ -1,10 +1,11 @@ library(flightplanning) f <- "mytest/lasek.gpkg" -f <- "/home/sapi/projekty/flightplanning-R/mytest/lasek.gpkg" roi <- sf::st_read(f) +if(nrow(roi) > 1) { + roi <- sf::st_union(roi) +} output <- "mytest/fly.csv" -output <- "/home/sapi/projekty/flightplanning-R/mytest/fly.csv" params <- flight.parameters( height = 120, @@ -21,13 +22,10 @@ litchi_sf(roi, flight.lines.angle = -1, max.waypoints.distance = 400, max.flight.time = 18, - grid = FALSE + grid = TRUE ) -if(nrow(roi) > 1) { - roi <- sf::st_union(roi) -} -litchi_sf(roi, +litchi.plan(roi, output, params, gimbal.pitch.angle = -90, @@ -37,6 +35,16 @@ litchi_sf(roi, grid = FALSE ) +litchi_sf(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = TRUE +) + # Create the csv plan From 97e70f0012bb8d3d349c71cc3df7214992d856d7 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Sun, 20 Nov 2022 22:53:35 +0100 Subject: [PATCH 15/35] TRUE/FALSE --- R/litchi_sf.R | 2 +- mytest/test_sf.R | 29 +++++++++++++++++------------ mytest/uav_test.R | 5 +++-- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/R/litchi_sf.R b/R/litchi_sf.R index 342aca3..0745116 100644 --- a/R/litchi_sf.R +++ b/R/litchi_sf.R @@ -76,7 +76,7 @@ litchi_sf = function(roi, sf::st_as_sf() } - if(sf::st_geometry_type(roi)[[1]] %in% c("POLYGON", "MULTIPOLYGON")) { + if(!sf::st_geometry_type(roi)[[1]] %in% c("POLYGON", "MULTIPOLYGON")) { stop("ROI is neither POLYGON nor MULTIPOLYGON") } if (!grepl("LENGTHUNIT[\"metre\",1]", sf::st_crs(roi)[2], fixed = TRUE)) diff --git a/mytest/test_sf.R b/mytest/test_sf.R index 6393bab..73b10a4 100644 --- a/mytest/test_sf.R +++ b/mytest/test_sf.R @@ -15,7 +15,7 @@ if(nrow(roi) > 1) { sf::st_as_sf() } - +roi params = flightplanning::flight.parameters(height=Wysokosc, flight.speed.kmh=24, side.overlap = 0.8, @@ -112,18 +112,22 @@ litchi_sf = function(roi, starting.point = 1, grid = FALSE) { - # Check parameters - roiCRS <- sf::st_crs(roi) + if (class(roi)[1] != "sf") { + roi <- sf::st_as_sf(roi) + } - # if (class(roi)[1] == "sf") { - # roi <- sf::as_Spatial(roi) - # } - # if (class(roi)[1] != "SpatialPolygonsDataFrame") - # stop("ROI is not a valid polygon layer") - # if (length(grep("units=m", as.character(roi@proj4string@projargs))) == 0) - # stop("ROI is not in a metric projection") - # if (methods::is(flight.params)[1] != "Flight Parameters") - # stop("Flight parameters is not an instance returned from flight.parameters()") + if(nrow(roi) > 1) { + roi <- roi[1,] |> + sf::st_as_sf() + } + + if(!sf::st_geometry_type(roi)[[1]] %in% c("POLYGON", "MULTIPOLYGON")) { + stop("ROI is neither POLYGON nor MULTIPOLYGON") + } + if (!grepl("LENGTHUNIT[\"metre\",1]", sf::st_crs(roi)[2], fixed = TRUE)) + stop("ROI is not in a metric projection") + if (methods::is(flight.params)[1] != "Flight Parameters") + stop("Flight parameters is not an instance returned from flight.parameters()") if (!is.logical(grid)) { stop("grid has to be TRUE or FALSE") } @@ -136,6 +140,7 @@ litchi_sf = function(roi, groundHeightOverlap = groundHeight * flight.params@front.overlap flightLineDistance = flight.params@flight.line.distance vertices <- sf::st_coordinates(roi)[,1:2] + roiCRS <- sf::st_crs(roi) # Get bounding box parameters if (flight.lines.angle != -1) { diff --git a/mytest/uav_test.R b/mytest/uav_test.R index 8ecbcad..ee573f6 100644 --- a/mytest/uav_test.R +++ b/mytest/uav_test.R @@ -1,6 +1,7 @@ library(flightplanning) -f <- "mytest/lasek.gpkg" -roi <- sf::st_read(f) +f <- "mytest/uav_bug.gpkg" +roi <- sf::st_read(f, layer = "Zalew") +str(roi) if(nrow(roi) > 1) { roi <- sf::st_union(roi) } From 72b86e6f491a9aba6d5966247e3ea2519859c99a Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Tue, 22 Nov 2022 21:23:22 +0100 Subject: [PATCH 16/35] readme --- README.md | 8 ++------ mytest/lasek.gpkg | Bin 106496 -> 106496 bytes mytest/test_sf.R | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 33d2e90..6eacd70 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ # flightplanning-R ================================ -[![CRAN](https://www.r-pkg.org/badges/version/flightplanning)](https://cran.r-project.org/web/packages/flightplanning) -[![Build Status](https://travis-ci.com/caiohamamura/flightplanning-R.svg)](https://travis-ci.com/caiohamamura/flightplanning-R) -[![codecov](https://codecov.io/gh/caiohamamura/flightplanning-R/branch/master/graph/badge.svg)](https://codecov.io/gh/caiohamamura/flightplanning-R) ![license](https://img.shields.io/badge/license-MIT-green.svg) -![Downloads](https://cranlogs.r-pkg.org/badges/grand-total/flightplanning) An R package for generating UAV flight plans, specially for Litchi. @@ -12,11 +8,11 @@ An R package for generating UAV flight plans, specially for Litchi. ## Installation -This package should be installed using the devtools. +This version of package should be installed using the devtools. ```r # install.packages("devtools") -devtools::install_github("caiohamamura/flightplanning-R") +devtools::install_github("gsapijaszko/flightplanning-R") ``` ## This fork diff --git a/mytest/lasek.gpkg b/mytest/lasek.gpkg index e313b637b773768c4ce0a5c04a75aec964a01e7b..8c748ea4f79fdea6aef4a0081be71172ab82e841 100644 GIT binary patch delta 22 ecmZoTz}9epZGtqT=tLQ3M$yKEtqF_^<^up$X$Oh` delta 22 ecmZoTz}9epZGtqT@I)DBM&ZVUtqF_^<^up$KL>^Y diff --git a/mytest/test_sf.R b/mytest/test_sf.R index 73b10a4..be7af35 100644 --- a/mytest/test_sf.R +++ b/mytest/test_sf.R @@ -23,7 +23,7 @@ params = flightplanning::flight.parameters(height=Wysokosc, flight.params <- params gimbal.pitch.angle = -90 -flight.lines.angle = -1 +flight.lines.angle = -15 max.waypoints.distance = 2000 max.flight.time = 15 starting.point = 1 @@ -37,7 +37,7 @@ flightplanning::litchi_sf(roi, max.waypoints.distance = 2000, max.flight.time = 15, starting.point = 1, - grid = FALSE + grid = TRUE ) From 30488b3018905ba0f4ef2e168adeff97e924e37d Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Tue, 22 Nov 2022 21:35:55 +0100 Subject: [PATCH 17/35] readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6eacd70..319a4ed 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ devtools::install_github("gsapijaszko/flightplanning-R") * added `grid = FALSE | TRUE` parameter, which sets the flying direction over polygon - DONE * added input for `sf` polygons -- `roi` can be read with `sf::st_read()` - DONE * try to replace {rgdal}, {rgeos} and {sp} with {sf} - DONE? +* if you prefer to run in in QGIS, please check the [QGIS processing script](https://github.com/gsapijaszko/qgis_r_processing/blob/main/uav_planner_litchi.rsx) available on [qgis r processing repo](https://github.com/gsapijaszko/qgis_r_processing). ## New function for testing * `litchi_sf()` From 5601588468a7a9718ce6d14a81f83aea8925fedc Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Tue, 22 Nov 2022 21:37:08 +0100 Subject: [PATCH 18/35] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 319a4ed..c4ad192 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # flightplanning-R -================================ + ![license](https://img.shields.io/badge/license-MIT-green.svg) An R package for generating UAV flight plans, specially for Litchi. From 6672a460359923d59dac233787b65922ab31ee09 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Wed, 23 Nov 2022 12:10:37 +0100 Subject: [PATCH 19/35] bug in row numbers --- R/litchi_sf.R | 28 +-- mytest/test_plan_sp.R | 437 ++++++++++++++++++++++++++++++++++++++++++ mytest/test_sf.R | 183 ++++++------------ mytest/uav_bug.gpkg | Bin 143360 -> 143360 bytes mytest/uav_test.R | 22 +-- 5 files changed, 518 insertions(+), 152 deletions(-) create mode 100644 mytest/test_plan_sp.R diff --git a/R/litchi_sf.R b/R/litchi_sf.R index 0745116..9e41278 100644 --- a/R/litchi_sf.R +++ b/R/litchi_sf.R @@ -57,14 +57,14 @@ #' #' @export litchi_sf = function(roi, - output, - flight.params, - gimbal.pitch.angle = -90, - flight.lines.angle = -1, - max.waypoints.distance = 2000, - max.flight.time = 15, - starting.point = 1, - grid = FALSE) { + output, + flight.params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + grid = FALSE) { # Check parameters if (class(roi)[1] != "sf") { @@ -195,7 +195,7 @@ litchi_sf = function(roi, inter <-suppressWarnings(sf::st_intersection( sf::st_buffer(sf::st_as_sf(roi), flightLineDistance), lines) |> - sf::st_cast(to = "LINESTRING")) + sf::st_cast(to = "LINESTRING")) # --------------------------------------------------------------------------------------------- # nLines <- length(inter) @@ -208,18 +208,22 @@ litchi_sf = function(roi, curvedPoints = outerCurvePoints(waypoints = waypoints, angle = alpha, flightLineDistance = flightLineDistance) + row.names(curvedPoints) <- curvedPoints$index # Adjust curve points position to avoid acute angles adjustedCurves = adjustAcuteAngles(xy = curvedPoints, angle = alpha, minAngle = 80) - + row.names(adjustedCurves) <- adjustedCurves$index + # dotąd się zgadza # Concatenate regular waypoints with curve waypoints wptsMatrix = as.data.frame(matrix(nrow=nrow(waypoints)+nrow(adjustedCurves),ncol=4)) colnames(wptsMatrix) = colnames=c("x", "y", "isCurve", "takePhoto") mat_pos = 1 for (i in seq_len(nrow(waypoints))) { - curve = as.vector(adjustedCurves[i, ]) + # i <- 6 + # mat_pos <- 11 + curve = as.vector(adjustedCurves[as.character(i), ]) hasCurve = !anyNA(curve) if (hasCurve) { if (curve$before) { @@ -259,7 +263,7 @@ litchi_sf = function(roi, waypoints2[-idx,] = waypoints waypoints = waypoints2 } -waypoints + waypoints # --------------------------------------------------------------------------------------------- t <- waypoints |> diff --git a/mytest/test_plan_sp.R b/mytest/test_plan_sp.R new file mode 100644 index 0000000..9c333c6 --- /dev/null +++ b/mytest/test_plan_sp.R @@ -0,0 +1,437 @@ +library(flightplanning) +f <- "mytest/uav_bug.gpkg" +roi <- sf::st_read(f, layer = "Zalew") |> + sf::st_as_sf() |> + sf::st_cast("POLYGON") +roi +str(roi) +if(nrow(roi) > 1) { + roi <- sf::st_union(roi) +} + +output <- "mytest/fly.csv" + +params <- flight.parameters( + height = 120, + focal.length35 = 24, + flight.speed.kmh = 24, + side.overlap = 0.8, + front.overlap = 0.8 +) + +litchi.plan(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = FALSE +) + +flight.params <- params +gimbal.pitch.angle = -90 +flight.lines.angle = -1 +max.waypoints.distance = 400 +max.flight.time = 18 +starting.point = 1 +grid = FALSE + + +litchi.plan = function(roi, + output, + flight.params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + grid = FALSE) { + # Check parameters + if (class(roi)[1] == "sf") { + roi <- sf::as_Spatial(roi) + } + + if (class(roi)[1] != "SpatialPolygonsDataFrame") + stop("ROI is not a valid polygon layer") + if (length(grep("units=m", as.character(roi@proj4string@projargs))) == 0) + stop("ROI is not in a metric projection") + if (methods::is(flight.params)[1] != "Flight Parameters") + stop("Flight parameters is not an instance returned from flight.parameters()") + # TODO add a test + if (!is.logical(grid)) { + stop("grid has to be TRUE or FALSE") + } + + # Parameters calculated + flight.speed.kmh = flight.params@flight.speed.kmh + flightSpeedMs = flight.speed.kmh / 3.6 + height = flight.params@height + groundHeight = flight.params@ground.height + groundHeightOverlap = groundHeight * flight.params@front.overlap + flightLineDistance = flight.params@flight.line.distance + vertices = roi@polygons[[1]]@Polygons[[1]]@coords + + # Get bounding box parameters + if (flight.lines.angle != -1) { + minBbox = getBBoxAngle(vertices, flight.lines.angle) + } else { + # if angle not specified use minimum possible bounding box + minBbox = shotGroups::getMinBBox(vertices) + # minBbox = getMinBBox(vertices) + } + + if (grid == FALSE) { + width = minBbox$height + height = minBbox$width + alpha = minBbox$angle + } else { + width = minBbox$width + height = minBbox$height + alpha = minBbox$angle-90 + } + + rads = alpha*pi/180 + centroid = apply(minBbox$pts, 2, mean) + + # Calculate points offset from centroid + # based on angle and width/height offsets + # width offsets (between flightlines) + nLines = ceiling(width / flightLineDistance) + 1 + xWidths = (-nLines/2):(nLines/2) * flightLineDistance + xWidths = rep(xWidths, each=2) + + # heights offset (one for upper half + # one for lower half) + heightDistance = groundHeight-groundHeightOverlap + heightAdjusted = height + 2*heightDistance + # Put offset to avoid intersection issues + heightAdjusted = heightAdjusted + heightDistance*2 + heightMHalf = -heightAdjusted/2 + heightPHalf = heightAdjusted/2 + yHeights = c(heightMHalf, heightPHalf) + + + # Switch position of the first point + if (starting.point == 2) { + yHeights = c(heightPHalf, heightMHalf) + } else if (starting.point == 3) { + xWidths = rev(xWidths) + yHeights = c(heightPHalf, heightMHalf) + } else if (starting.point == 4) { + xWidths = rev(xWidths) + } + + # Interleave one upper, two bottom + # two upper, two bottom... until end + yHeights = c(rep(c(yHeights, rev(yHeights)), nLines/2+1)) + yHeights = yHeights[1:length(xWidths)] + + + # Calculate translated x and y from + # angles and offsets from centroid + xys = data.frame( + x = -xWidths * sin(rads) + + yHeights * cos(rads), + y = xWidths * cos(rads) + + yHeights * sin(rads)) + + # Initial waypoints to intersect waypoints + waypoints = xys + rep(centroid, each=nrow(xys)) + + ################################################# + # Intersect each flight line from bounding box + # to match actual ROI + ################################################# + # For some reason gIntersection with MULTILINESTRING + # will return linestrings in inconsistent order + # though it will be done in a for loop + + # + # + # wktLines = paste(apply(waypoints, 1, paste, collapse=" "), collapse=", ") + # wktLines = paste("LINESTRING(", wktLines,")") + # gLines = rgeos::readWKT(wktLines, p4s = roi@proj4string) + # inter = rgeos::gIntersection(rgeos::gBuffer(roi, width = flightLineDistance), gLines) + # nLines = length(inter@lines[[1]]@Lines) + + # flightLines = t(sapply(inter@lines[[1]]@Lines, function(x) x@coords)) + + # RSB + glist <- vector(mode="list", length=nrow(waypoints)-1) + for (i in seq_along(glist)) glist[[i]] <- sp::Lines(list(sp::Line(waypoints[c(i, (i+1)),])), ID=as.character(i)) + gLines <- sp::SpatialLines(glist, proj4string=slot(roi, "proj4string")) + inter = rgeos::gIntersection(rgeos::gBuffer(roi, width = flightLineDistance), gLines, byid=TRUE) + nLines <- length(inter) + flightLines <- t(sapply(slot(inter, "lines"), function(x) slot(slot(x, "Lines")[[1]], "coords"))) + + # RSB + flightLines = flightLines[,c(1,3,2,4)] + + + waypoints = matrix(nrow=nLines * 2, ncol=2) + waypoints[seq(1, nLines*2, 2),] = flightLines[, 1:2] + waypoints[seq(2, nLines*2, 2),] = flightLines[, 3:4] + + # Calculate curves points to allow smooth curves + curvedPoints = flightplanning:::outerCurvePoints(waypoints = waypoints, + angle = alpha, + flightLineDistance = flightLineDistance) + + # Adjust curve points position to avoid acute angles + adjustedCurves = flightplanning:::adjustAcuteAngles(xy = curvedPoints, + angle = alpha, + minAngle = 80) + + # Concatenate regular waypoints with curve waypoints + wgs84 = "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0" + wptsMatrix = as.data.frame(matrix(nrow=nrow(waypoints)+nrow(adjustedCurves),ncol=4)) + colnames(wptsMatrix) = colnames=c("x", "y", "isCurve", "takePhoto") + mat_pos = 1 + for (i in seq_len(nrow(waypoints))) { + curve = as.vector(adjustedCurves[as.character(i),]) + + # From R 4.2.0 onwards: + # "as.vector() gains a data.frame method which returns a simple + # named list, also clearing a long standing ‘FIXME’ to enable + # as.vector(, mode="list"). This breaks code relying + # on as.vector() to return the unchanged data frame." + # therefore changing curve[,1:2] to curve[1,2] and removing cbind + + hasCurve = !anyNA(curve) + if (hasCurve) { + if (curve$before) { + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) + mat_pos = mat_pos + 1 + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + } else { + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) + mat_pos = mat_pos + 1 + } + } else { + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + } + } + waypoints = wptsMatrix + + # Break if distance greater than the maxWaypointDistance + waypointsXY = waypoints[, c("x", "y")] + distances = sqrt(diff(waypoints$x)**2 + diff(waypoints$y)**2) + breakIdx = distances > max.waypoints.distance + + newSize = nrow(waypoints) + sum(breakIdx) + if (newSize != nrow(waypoints)) { + midpoints = (waypointsXY[breakIdx,] + waypointsXY[-1,][breakIdx,])/2 + waypoints2 = data.frame(x = numeric(newSize), + y = numeric(newSize), + isCurve = FALSE, + takePhoto = TRUE) + + pos = seq_along(breakIdx)[breakIdx] + idx = pos + order(pos) + waypoints2[idx,1:2] = midpoints + waypoints2[-idx,] = waypoints + waypoints = waypoints2 + } + + + # Transform to WGS84 latitude and longitude + transform = rgdal::rawTransform(roi@proj4string@projargs, wgs84, n=nrow(waypoints), x=waypoints[,1], y=waypoints[,2]) + lats = transform[[2]] + lngs = transform[[1]] + graphics::plot(waypoints[,1:2]) + graphics::polygon(roi@polygons[[1]]@Polygons[[1]]@coords) + + + # Calculate heading + nWaypoints = nrow(waypoints) + latDiff = lats[-1]-lats[-nWaypoints] + lngDiff = lngs[-1]-lngs[-nWaypoints] + headDegree = atan(latDiff/lngDiff)/pi*180 + finalHeading = 270-headDegree + finalHeading[lngDiff > 0] = 90-headDegree[lngDiff > 0] + + # Set parameters of the flight in the CSV + dfLitchi = read.csv(system.file("extdata/litchi.csv", package = "flightplanning")) + dfLitchi = dfLitchi[rep(1, length(lats)),] + dfLitchi$latitude = lats + dfLitchi$longitude = lngs + dfLitchi$altitude.m. = flight.params@height + dfLitchi$speed.m.s. = flightSpeedMs + dfLitchi$heading.deg. = c(finalHeading, 90) + dfLitchi$curvesize.m. = 0 + dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 + dfLitchi$photo_distinterval = flight.params@ground.height + dfLitchi$gimbalpitchangle = gimbal.pitch.angle + + + # Split the flight if is too long + dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) + distAcum = c(0,cumsum(dists)) + flightTime = distAcum / (flightSpeedMs*0.75) / 60 + finalSize = nrow(dfLitchi) + totalFlightTime = flightTime[finalSize] + dfLitchi$split = 1 + if (totalFlightTime > max.flight.time) { + indexes = seq_len(finalSize) + nBreaks = ceiling(totalFlightTime/max.flight.time) + breaks = seq(0, flightTime[finalSize], length.out = nBreaks+1)[c(-1, -nBreaks-1)] + endWaypointsIndex = indexes[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] + endWaypoints = flightTime[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] + selected = sapply(breaks, function(x) which.min(abs(endWaypoints-x))) + waypointsBreak = endWaypointsIndex[indexes[selected]] + + + dfLitchi$split = rep(1:nBreaks, diff(c(0, waypointsBreak, finalSize))) + splits = split.data.frame(dfLitchi, f = dfLitchi$split) + message("Your flight was splitted in ", length(splits), " splits, +because the total time would be ", round(totalFlightTime, 2), " minutes.") + message("They were saved as:") + first = substr(output, 1, nchar(output)-4) + second = substr(output, nchar(output)-3, nchar(output)) + for (dataSplit in splits) { + i = dataSplit[1, ]$split + output2 = paste0(first, i, second) + write.csv(dataSplit[,-ncol(dataSplit)], output2, row.names = FALSE) + message(output2) + } + output2 = paste0(first, "_entire", second) + write.csv(dfLitchi, output2, row.names = FALSE) + message("The entire flight plan was saved as:") + message(output2) + } else { + write.csv(dfLitchi, output, row.names = FALSE) + } + + colors = grDevices::rainbow(length(unique(dfLitchi$split))) + for (i in unique(dfLitchi$split)) + { + graphics::lines(waypoints[dfLitchi$split == i,1:2], lty=2, col=colors[as.integer(i)]) + } + graphics::text(waypoints[,1], waypoints[,2], seq_along(waypoints[,1]), pos=3) + + + message("#####################") + message("## Flight settings ## ") + message("#####################") + message("Min shutter speed: ", appendLF = FALSE) + message(flight.params@minimum.shutter.speed) + message("Photo interval: ", appendLF = FALSE) + message(flight.params@photo.interval, appendLF = FALSE) + message(" s") + message("Flight speed: ", appendLF = FALSE) + message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) + message(" km/h") + message("Flight lines angle: ", appendLF = FALSE) + message(round(alpha, 4)) + message('Total flight time: ', appendLF = FALSE) + message(round(totalFlightTime, 4)) + message('Distance between flyin lines: ', appendLF = FALSE) + message(round(flightLineDistance, 3)) + message('', appendLF = TRUE) + + return (waypoints) +} + + + +#' Function to calculate flight parameters +#' +#' This function will calculate the flight parameters by providing the camera settings +#' target flight height or gsd, front and side overlap. +#' +#' @rdname flight.parameters +#' +#' @param gsd target ground resolution in centimeters, must provide either `gsd` or `height` +#' @param height target flight height, default NA +#' @param focal.length35 numeric. Camera focal length 35mm equivalent, default 20 +#' @param image.width.px numeric. Image width in pixels, default 4000 +#' @param image.height.px numeric. Image height in pixels, default 3000 +#' @param side.overlap desired width overlap between photos, default 0.8 +#' @param front.overlap desired height overlap between photos, default 0.8 +#' @param flight.speed.kmh flight speed in km/h, default 54. +#' +#' @examples +#' params = flight.parameters( +#' gsd = 4, +#' side.overlap = 0.8, +#' front.overlap = 0.8, +#' flight.speed.kmh = 54 +#' ) +#' +#' @export +flight.parameters = function( + height = NA, + gsd = NA, + focal.length35 = 20, + image.width.px = 4000, + image.height.px = 3000, + side.overlap = 0.8, + front.overlap = 0.8, + flight.speed.kmh = 54) { + + if (is.na(gsd) == is.na(height)) { + stop("You must specify either gsd or height!") + } + + + image.diag.px = sqrt(image.width.px^2 + image.height.px^2) + if (is.na(gsd)) { + mult.factor = (height / focal.length35) + diag.ground = DIAG_35MM * mult.factor + gsd = diag.ground / image.diag.px * 100 + groundWidth = image.width.px * gsd / 100 + } else { + groundWidth = image.width.px * gsd / 100 + diag.ground = image.diag.px * gsd / 100 + mult.factor = diag.ground / DIAG_35MM + height = mult.factor * focal.length35 + } + + flightLineDistance = groundWidth - side.overlap * groundWidth + + flightSpeedMs = flight.speed.kmh / 3.6 + speedPxPerSecond = flightSpeedMs / (gsd*0.01) + + # FIGUEIREDO, E. O. et al. + # Planos de Voo Semiautônomos para Fotogrametria + # com Aeronaves Remotamente Pilotadas de Classe 3 + maxPixelRoll = 1.2 + minimumShutterSpeed = paste("1/",round(speedPxPerSecond/maxPixelRoll), sep="") + + groundHeight = image.height.px * gsd / 100 + groundHeightOverlap = groundHeight * front.overlap + groundAllowedOffset = groundHeight - groundHeightOverlap + photoInterval = groundAllowedOffset / flightSpeedMs + if (photoInterval < MIN_PHOTO_INTERVAL) { + photoInterval = 2 + flightSpeedMs = groundAllowedOffset / photoInterval + flight.speed.kmh = flightSpeedMs*3.6 + warning(paste0("Speed had to be lowered because frequency of photos would be too high + New speed: ", flight.speed.kmh, "km/h")) + } else if ((photoInterval %% 1) > 1e-4) { + photoInterval = ceiling(photoInterval) + flightSpeedMs = groundAllowedOffset / photoInterval + flight.speed.kmh = flightSpeedMs*3.6 + warning(paste0("Speed lowered to ", flight.speed.kmh, "km/h to round up photo interval time\n")) + } + + params = methods::new("Flight Parameters") + params@height = height + params@gsd = gsd + params@flight.line.distance = flightLineDistance + params@minimum.shutter.speed = minimumShutterSpeed + params@photo.interval = photoInterval + params@ground.height = groundHeight + params@front.overlap = front.overlap + params@flight.speed.kmh = flight.speed.kmh + + return (params) +} + +f <- list.files(path = "mytest", pattern = ".csv", full.names = TRUE) +unlink(f) diff --git a/mytest/test_sf.R b/mytest/test_sf.R index be7af35..277a499 100644 --- a/mytest/test_sf.R +++ b/mytest/test_sf.R @@ -1,117 +1,54 @@ -# --------------------------------------------------------------------------------------------- -# parametry - -Wysokosc <- 100 -output = ("mytest/lot.csv") -roi = sf::st_read("mytest/lasek.gpkg") -roi = sf::st_read(system.file("extdata", "exampleBoundary.shp", package="flightplanning")) +library(flightplanning) +f <- "mytest/uav_bug.gpkg" +roi <- sf::st_read(f, layer = "Zalew") |> + sf::st_as_sf() |> + sf::st_cast("POLYGON") +roi +str(roi) if(nrow(roi) > 1) { - roi <- sf::st_union(roi) |> - sf::st_as_sf() + roi <- sf::st_union(roi) } -if(nrow(roi) > 1) { - roi <- roi[1,] |> - sf::st_as_sf() -} +output <- "mytest/fly.csv" -roi -params = flightplanning::flight.parameters(height=Wysokosc, - flight.speed.kmh=24, - side.overlap = 0.8, - front.overlap = 0.8) +params <- flight.parameters( + height = 120, + focal.length35 = 24, + flight.speed.kmh = 24, + side.overlap = 0.91, + front.overlap = 0.7 +) + +litchi_sf(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = FALSE +) flight.params <- params gimbal.pitch.angle = -90 -flight.lines.angle = -15 -max.waypoints.distance = 2000 -max.flight.time = 15 +flight.lines.angle = -1 +max.waypoints.distance = 400 +max.flight.time = 18 starting.point = 1 grid = FALSE -flightplanning::litchi_sf(roi, - output, - params, - gimbal.pitch.angle = -90, - flight.lines.angle = -1, - max.waypoints.distance = 2000, - max.flight.time = 15, - starting.point = 1, - grid = TRUE -) - -# --------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------- -#' Function to generate Litchi csv flight plan -#' -#' @rdname litchi.sf -#' -#' @return A data frame with the waypoints calculated for the flight plan -#' -#' @param roi range of interest loaded as an OGR layer, must be in -#' a metric units projection for working properly -#' @param output output path for the csv file -#' @param flight.params Flight Parameters. parameters calculated from flight.parameters() -#' @param gimbal.pitch.angle gimbal angle for taking photos, default -90 (overriden at flight time) -#' @param flight.lines.angle angle for the flight lines, default -1 (auto set based on larger direction) -#' @param max.waypoints.distance maximum distance between waypoints in meters, -#' default 2000 (some issues have been reported with distances > 2 Km) -#' @param max.flight.time maximum flight time. If mission is greater than the estimated -#' time, it will be splitted into smaller missions. -#' @param starting.point numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 -#' @param grid logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular -#' -#' @note this function will feed the csv flight plan with the `gimbal.pitch.angle` -#' and the `photo time interval` for each waypoint, but those are not supported -#' by Litchi yet, although they are present in the exported csv from the -#' Litchi hub platform, though it may be supported in the future; when it does -#' the function will already work with this feature. -#' -#' @examples -#' library(flightplanning) -#' -#' exampleBoundary = sf::st_read( -#' system.file("extdata", -#' "exampleBoundary.shp", -#' package="flightplanning" -#' ), -#' "exampleBoundary") -#' outPath = tempfile(fileext=".csv") -#' -#' flight.params = flight.parameters( -#' gsd = 4, -#' side.overlap = 0.8, -#' front.overlap = 0.8, -#' flight.speed.kmh = 54 -#' ) -#' -#' lichi.sf(exampleBoundary, -#' outPath, -#' flight.params, -#' flight.lines.angle = -1, -#' max.waypoints.distance = 2000, -#' max.flight.time = 15) -#' -#' -#' @export -#' @import sf -#' @importFrom graphics text -#' @importFrom shotGroups getMinBBox -#' @importFrom methods slot -#' @importFrom sp Line Lines SpatialLines -#' @importFrom utils data read.csv write.csv -#' litchi_sf = function(roi, - output, - flight.params, - gimbal.pitch.angle = -90, - flight.lines.angle = -1, - max.waypoints.distance = 2000, - max.flight.time = 15, - starting.point = 1, - grid = FALSE) { - + output, + flight.params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + grid = FALSE) { + + # Check parameters if (class(roi)[1] != "sf") { roi <- sf::st_as_sf(roi) } @@ -144,7 +81,7 @@ litchi_sf = function(roi, # Get bounding box parameters if (flight.lines.angle != -1) { - minBbox = flightplanning:::getBBoxAngle(vertices, flight.lines.angle) + minBbox = getBBoxAngle(vertices, flight.lines.angle) } else { # if angle not specified use minimum possible bounding box minBbox = shotGroups::getMinBBox(vertices) @@ -237,35 +174,38 @@ litchi_sf = function(roi, lines$ID <- seq.int(nrow(lines)) # --------------------------------------------------------------------------------------------- - inter <- suppressWarnings(sf::st_intersection( - sf::st_buffer(sf::st_as_sf(roi), flightLineDistance), - lines) - ) |> subset(select = c("ID", "geometry")) |> - sf::st_cast(to = "LINESTRING") - inter + inter <-suppressWarnings(sf::st_intersection( + sf::st_buffer(sf::st_as_sf(roi), flightLineDistance), + lines) |> + sf::st_cast(to = "LINESTRING")) + # --------------------------------------------------------------------------------------------- - nLines <- length(inter) + # nLines <- length(inter) # gflightLines <- - waypoints <- sf::st_coordinates(sf::st_as_sf(inter))[,1:2] + waypoints <- sf::st_coordinates(inter)[,1:2] # --------------------------------------------------------------------------------------------- # Calculate curves points to allow smooth curves curvedPoints = flightplanning:::outerCurvePoints(waypoints = waypoints, - angle = alpha, - flightLineDistance = flightLineDistance) + angle = alpha, + flightLineDistance = flightLineDistance) + row.names(curvedPoints) <- curvedPoints$index # Adjust curve points position to avoid acute angles adjustedCurves = flightplanning:::adjustAcuteAngles(xy = curvedPoints, - angle = alpha, - minAngle = 80) - + angle = alpha, + minAngle = 80) + row.names(adjustedCurves) <- adjustedCurves$index + # dotąd się zgadza # Concatenate regular waypoints with curve waypoints wptsMatrix = as.data.frame(matrix(nrow=nrow(waypoints)+nrow(adjustedCurves),ncol=4)) colnames(wptsMatrix) = colnames=c("x", "y", "isCurve", "takePhoto") mat_pos = 1 for (i in seq_len(nrow(waypoints))) { - curve = as.vector(adjustedCurves[i, ]) +# i <- 6 +# mat_pos <- 11 + curve = as.vector(adjustedCurves[as.character(i), ]) hasCurve = !anyNA(curve) if (hasCurve) { if (curve$before) { @@ -305,7 +245,7 @@ litchi_sf = function(roi, waypoints2[-idx,] = waypoints waypoints = waypoints2 } -waypoints + waypoints # --------------------------------------------------------------------------------------------- t <- waypoints |> @@ -406,12 +346,5 @@ because the total time would be ", round(totalFlightTime, 2), " minutes.") } -rename_geometry <- function(df, name){ - current = attr(df, "sf_column") - names(df)[names(df) == current] = name - sf::st_geometry(df) = name - df -} - f <- list.files(path = "mytest", pattern = ".csv", full.names = TRUE) unlink(f) diff --git a/mytest/uav_bug.gpkg b/mytest/uav_bug.gpkg index fa686fa14da30196fc1775dbde1fd49fe3097a14..d082489c4f7168603331eb7c1e7e7fc9f6faf1cb 100644 GIT binary patch delta 50 zcmZp8z|ru4Lnb)TC$l6~AuYcsH?c&)m_dMniHX5ML4kpRL29CmGow^vLTdu!)&!;n F`T%*m4S@gv delta 50 zcmZp8z|ru4Lnb)TC$l6~AuYcsH?c&)m_dMnk&(ecL4kpRL2{yuGoxf + sf::st_as_sf() |> + sf::st_cast("POLYGON") +roi str(roi) if(nrow(roi) > 1) { roi <- sf::st_union(roi) @@ -12,8 +15,8 @@ params <- flight.parameters( height = 120, focal.length35 = 24, flight.speed.kmh = 24, - side.overlap = 0.8, - front.overlap = 0.8 + side.overlap = 0.6, + front.overlap = 0.6 ) litchi_sf(roi, @@ -23,7 +26,7 @@ litchi_sf(roi, flight.lines.angle = -1, max.waypoints.distance = 400, max.flight.time = 18, - grid = TRUE + grid = FALSE ) litchi.plan(roi, @@ -46,14 +49,3 @@ litchi_sf(roi, grid = TRUE ) - - -# Create the csv plan -flightplanning::litchi.plan(roi, - output, - params, - gimbal.pitch.angle = -90, - flight.lines.angle = -1, - max.waypoints.distance = 400, - max.flight.time = 18, - grid = FALSE) From 6aea0d6872a7c60199d03e579d8e413a199854fc Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Fri, 25 Nov 2022 14:54:50 +0100 Subject: [PATCH 20/35] mytest --- mytest/lasek.gpkg | Bin 106496 -> 106496 bytes mytest/test_plan_sp.R | 10 ++-------- mytest/uav_bug.gpkg | Bin 143360 -> 143360 bytes mytest/uav_test.R | 34 +++++++++++++++++++++++++++------- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/mytest/lasek.gpkg b/mytest/lasek.gpkg index 8c748ea4f79fdea6aef4a0081be71172ab82e841..7dc8ac733c70a9ddd9e88063890dd0c8b4eddc9b 100644 GIT binary patch delta 22 ecmZoTz}9epZGtqT, mode="list"). This breaks code relying - # on as.vector() to return the unchanged data frame." - # therefore changing curve[,1:2] to curve[1,2] and removing cbind - hasCurve = !anyNA(curve) if (hasCurve) { if (curve$before) { diff --git a/mytest/uav_bug.gpkg b/mytest/uav_bug.gpkg index d082489c4f7168603331eb7c1e7e7fc9f6faf1cb..ac22f493ce7f075eb1e3a8cf6a13a8b29fb33e40 100644 GIT binary patch delta 50 zcmZp8z|ru4Lnb)TC$l6~AuYcsH?c&)m_dMnk&(ecL4kpRL3*N$Goy54LTdu!)&!;n F`T%*Q4S@gv delta 50 zcmZp8z|ru4Lnb)TC$l6~AuYcsH?c&)m_dMniHX5ML4kpRL29CmGow^vLTdu!)&!;n F`T%*m4S@gv diff --git a/mytest/uav_test.R b/mytest/uav_test.R index e9bd694..430ffc1 100644 --- a/mytest/uav_test.R +++ b/mytest/uav_test.R @@ -1,9 +1,6 @@ library(flightplanning) f <- "mytest/uav_bug.gpkg" -roi <- sf::st_read(f, layer = "Zalew") |> - sf::st_as_sf() |> - sf::st_cast("POLYGON") -roi +roi <- sf::st_read(f, layer = "Zalew") str(roi) if(nrow(roi) > 1) { roi <- sf::st_union(roi) @@ -15,8 +12,8 @@ params <- flight.parameters( height = 120, focal.length35 = 24, flight.speed.kmh = 24, - side.overlap = 0.6, - front.overlap = 0.6 + side.overlap = 0.7, + front.overlap = 0.7 ) litchi_sf(roi, @@ -29,6 +26,18 @@ litchi_sf(roi, grid = FALSE ) +litchi_sf(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = TRUE +) + + + litchi.plan(roi, output, params, @@ -46,6 +55,17 @@ litchi_sf(roi, flight.lines.angle = -1, max.waypoints.distance = 400, max.flight.time = 18, - grid = TRUE + grid = FALSE ) + + +# Create the csv plan +flightplanning::litchi.plan(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = FALSE) From 9febed69f95fa8b8bb88aab51eeac47e3737c30d Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Fri, 25 Nov 2022 17:52:46 +0100 Subject: [PATCH 21/35] incorporated changes from Hivemapper --- DESCRIPTION | 6 +- R/litchi_sf.R | 251 ++++++++++++++++++----- R/main.R | 40 +++- R/utils.R | 27 +++ README.md | 16 +- man/flight.parameters.Rd | 8 +- man/litchi_sf.Rd | 3 + mytest/test_plan_sp.R | 431 +++++++++++++++++++++++++++++++++++++++ mytest/test_sf.R | 309 +++++++++++++++++++++------- tests/testthat/test.R | 5 +- 10 files changed, 941 insertions(+), 155 deletions(-) create mode 100644 mytest/test_plan_sp.R diff --git a/DESCRIPTION b/DESCRIPTION index 054dd9a..c15d1db 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,13 +1,15 @@ Package: flightplanning Type: Package Title: UAV Flight Planning -Version: 0.8.4 +Version: 0.9 Authors@R: c( person("Caio", "Hamamura", email = "caiohamamura@gmail.com", role = c("aut", "cre")), person("Danilo Roberti Alves de", "Almeida", email = "daniloflorestas@gmail.com", role = c("aut")), person("Daniel de Almeida", "Papa", email = "daniel.papa@embrapa.br", role = c("aut")), person("Hudson Franklin Pessoa", "Veras", email = "hudson@engeverde.com", role = c("aut")), person("Evandro Orfanó", "Figueiredo", email = "evandro.figueiredo@embrapa.br", role = c("aut"))) + person("Tomasz", "Nycz", email = "", role = c(ctb)) + person("Grzegorz", "Sapijaszko", email = "grzegorz@sapijaszko.net", role = c("ctb")) Description: Utility functions for creating flight plans for unmanned aerial vehicles (UAV), specially for the Litchi Hub platform. It calculates the flight and camera settings based on the camera specifications, exporting the flight plan CSV format ready to import into Litchi Hub. Imports: graphics, @@ -23,6 +25,6 @@ Suggests: testthat License: MIT + file LICENSE Encoding: UTF-8 LazyData: true -RoxygenNote: 7.2.1 +RoxygenNote: 7.2.2 URL: https://github.com/caiohamamura/flightplanning-R BugReports: https://github.com/caiohamamura/flightplanning-R/issues diff --git a/R/litchi_sf.R b/R/litchi_sf.R index 0745116..a836949 100644 --- a/R/litchi_sf.R +++ b/R/litchi_sf.R @@ -1,3 +1,6 @@ +MAX_WAYPOINTS = 99 + + #' Function to generate Litchi csv flight plan #' #' @rdname litchi_sf @@ -15,6 +18,7 @@ #' @param max.flight.time maximum flight time. If mission is greater than the estimated #' time, it will be splitted into smaller missions. #' @param starting.point numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 +#' @param launch list(0,0) launch point coordinates (x, y), has to be provided in the same metric CRS as roi #' @param grid logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular #' #' @note this function will feed the csv flight plan with the `gimbal.pitch.angle` @@ -52,19 +56,19 @@ #' @importFrom graphics text #' @importFrom shotGroups getMinBBox #' @importFrom methods slot -#' @importFrom sp Line Lines SpatialLines #' @importFrom utils data read.csv write.csv #' #' @export litchi_sf = function(roi, - output, - flight.params, - gimbal.pitch.angle = -90, - flight.lines.angle = -1, - max.waypoints.distance = 2000, - max.flight.time = 15, - starting.point = 1, - grid = FALSE) { + output, + flight.params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + launch = list(0, 0), + grid = FALSE) { # Check parameters if (class(roi)[1] != "sf") { @@ -122,6 +126,8 @@ litchi_sf = function(roi, # based on angle and width/height offsets # width offsets (between flightlines) nLines = ceiling(width / flightLineDistance) + 1 + # Then need to update flightLineDistance to avoid offset/quantization errors + flightLineDistance = width / (nLines - 1) xWidths = (-nLines/2):(nLines/2) * flightLineDistance xWidths = rep(xWidths, each=2) @@ -137,6 +143,12 @@ litchi_sf = function(roi, # Switch position of the first point + if (starting.point == 0) { + # In this case we will automatically pick the best starting point + # TODO check if launch is valid and not (0,0) + # TODO figure out closest corner in shape to launch point and then set starting.point to 1-4 + starting.point == 1 + } if (starting.point == 2) { yHeights = c(heightPHalf, heightMHalf) } else if (starting.point == 3) { @@ -163,14 +175,6 @@ litchi_sf = function(roi, # Initial waypoints to intersect waypoints waypoints = xys + rep(centroid, each=nrow(xys)) - ################################################# - # Intersect each flight line from bounding box - # to match actual ROI - ################################################# - # For some reason gIntersection with MULTILINESTRING - # will return linestrings in inconsistent order - # though it will be done in a for loop - # --------------------------------------------------------------------------------------------- lines <- do.call( sf::st_sfc, @@ -195,31 +199,33 @@ litchi_sf = function(roi, inter <-suppressWarnings(sf::st_intersection( sf::st_buffer(sf::st_as_sf(roi), flightLineDistance), lines) |> - sf::st_cast(to = "LINESTRING")) + sf::st_cast(to = "LINESTRING")) # --------------------------------------------------------------------------------------------- - # nLines <- length(inter) - # gflightLines <- waypoints <- sf::st_coordinates(inter)[,1:2] # --------------------------------------------------------------------------------------------- # Calculate curves points to allow smooth curves curvedPoints = outerCurvePoints(waypoints = waypoints, - angle = alpha, - flightLineDistance = flightLineDistance) + angle = alpha, + flightLineDistance = flightLineDistance) + row.names(curvedPoints) <- curvedPoints$index # Adjust curve points position to avoid acute angles adjustedCurves = adjustAcuteAngles(xy = curvedPoints, - angle = alpha, - minAngle = 80) + angle = alpha, + minAngle = 80) + row.names(adjustedCurves) <- adjustedCurves$index # Concatenate regular waypoints with curve waypoints wptsMatrix = as.data.frame(matrix(nrow=nrow(waypoints)+nrow(adjustedCurves),ncol=4)) colnames(wptsMatrix) = colnames=c("x", "y", "isCurve", "takePhoto") mat_pos = 1 for (i in seq_len(nrow(waypoints))) { - curve = as.vector(adjustedCurves[i, ]) + # i <- 6 + # mat_pos <- 11 + curve = as.vector(adjustedCurves[as.character(i), ]) hasCurve = !anyNA(curve) if (hasCurve) { if (curve$before) { @@ -240,26 +246,47 @@ litchi_sf = function(roi, } waypoints = wptsMatrix + # from https://github.com/caiohamamura/flightplanning-R/pull/4/commits/c36501d8ea0cab6cdcb3e5123452bb4c18599aac + # # Break if distance greater than the maxWaypointDistance - waypointsXY = waypoints[, c("x", "y")] - distances = sqrt(diff(waypoints$x)**2 + diff(waypoints$y)**2) - breakIdx = distances > max.waypoints.distance - - newSize = nrow(waypoints) + sum(breakIdx) - if (newSize != nrow(waypoints)) { - midpoints = (waypointsXY[breakIdx,] + waypointsXY[-1,][breakIdx,])/2 - waypoints2 = data.frame(x = numeric(newSize), - y = numeric(newSize), - isCurve = FALSE, - takePhoto = TRUE) - - pos = seq_along(breakIdx)[breakIdx] - idx = pos + order(pos) - waypoints2[idx,1:2] = midpoints - waypoints2[-idx,] = waypoints - waypoints = waypoints2 + # A single pass only adds one intermediate waypoint even if a leg is longer than max, + # but more than one intermediate point may be needed. + # We can iterate this process as a temp fix but we may be adding more intermediate + # waypoints than strictly necessary--e.g. when 2 intermediate points will suffice we will get 3. + retest = TRUE + while (retest) { + waypointsXY = waypoints[, c("x", "y")] + distances = sqrt(diff(waypoints$x)**2 + diff(waypoints$y)**2) + breakIdx = distances > max.waypoints.distance + + newSize = nrow(waypoints) + sum(breakIdx) + if (newSize != nrow(waypoints)) { + midpoints = (waypointsXY[breakIdx,] + waypointsXY[-1,][breakIdx,])/2 + waypoints2 = data.frame(x = numeric(newSize), + y = numeric(newSize), + isCurve = FALSE, + takePhoto = TRUE) + + pos = seq_along(breakIdx)[breakIdx] + idx = pos + order(pos) + waypoints2[idx,1:2] = midpoints + waypoints2[-idx,] = waypoints + waypoints = waypoints2 + } + else { + retest = FALSE + } + } + # from: https://github.com/caiohamamura/flightplanning-R/pull/4/commits/fed8f6928aef89eb5d7884b6001b7e16f0ec4bfd + # + # Check if launch point has been specified before inserting it as way-point 1 + hasCustomLaunch = (launch[1] != 0) || (launch[2] != 0) + if (hasCustomLaunch) { + message("Launch point specified: ", launch[1], ',', launch[2]) + MAX_WAYPOINTS = MAX_WAYPOINTS - 1 + } else { + message("No launch point specified") } -waypoints # --------------------------------------------------------------------------------------------- t <- waypoints |> @@ -267,6 +294,7 @@ waypoints sf::st_transform(crs = "EPSG:4326") lngs <- as.numeric(sf::st_coordinates(t)[,1]) lats <- as.numeric(sf::st_coordinates(t)[,2]) + photos = t$takePhoto # --------------------------------------------------------------------------------------------- graphics::plot(waypoints[,1:2]) @@ -286,13 +314,16 @@ waypoints dfLitchi$latitude = lats dfLitchi$longitude = lngs dfLitchi$altitude.m. = flight.params@height + dfLitchi$altitudemode = 1 dfLitchi$speed.m.s. = flightSpeedMs dfLitchi$heading.deg. = c(finalHeading, 90) dfLitchi$curvesize.m. = 0 dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 - dfLitchi$photo_distinterval = flight.params@ground.height + dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs * photos + dfLitchi$photo_timeinterval = flight.params@photo.interval * photos dfLitchi$gimbalpitchangle = gimbal.pitch.angle - + dfLitchi$actiontype1 = 5 + dfLitchi$actionparam1 = gimbal.pitch.angle # Split the flight if is too long dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) @@ -301,22 +332,131 @@ waypoints finalSize = nrow(dfLitchi) totalFlightTime = flightTime[finalSize] dfLitchi$split = 1 - if (totalFlightTime > max.flight.time) { + if ((totalFlightTime > max.flight.time) || (nrow(waypoints) > MAX_WAYPOINTS)) { indexes = seq_len(finalSize) - nBreaks = ceiling(totalFlightTime/max.flight.time) + nBreaks = max(ceiling(totalFlightTime/max.flight.time), ceiling(nrow(waypoints)/MAX_WAYPOINTS)) breaks = seq(0, flightTime[finalSize], length.out = nBreaks+1)[c(-1, -nBreaks-1)] endWaypointsIndex = indexes[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] endWaypoints = flightTime[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] selected = sapply(breaks, function(x) which.min(abs(endWaypoints-x))) waypointsBreak = endWaypointsIndex[indexes[selected]] - dfLitchi$split = rep(1:nBreaks, diff(c(0, waypointsBreak, finalSize))) splits = split.data.frame(dfLitchi, f = dfLitchi$split) - message("Your flight was splitted in ", length(splits), " splits, -because the total time would be ", round(totalFlightTime, 2), " minutes.") - message("They were saved as:") - first = substr(output, 1, nchar(output)-4) + + +# --------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------- + if (hasCustomLaunch) { + p0x = launch[[1]][1] + p0y = launch[[2]][1] + + message("adding custom launch point to submissions") + + launch84 <- sf::st_point(x = c(launch[[1]], launch[[2]])) |> + sf::st_sfc(crs = roiCRS) |> + sf::st_transform(crs = "EPSG:4326") |> + sf::st_coordinates() + + overage = NULL + + for (i in 1:length(splits)) { + message("starting ", i) + if (!is.null(overage)) { + message("setting ", i, " to ", "overage (", nrow(overage), ") and ", nrow(splits[[i]])) + splits[[i]] = rbind(overage, splits[[i]]) + } + + mercator = splits[[i]] |> + sf::st_as_sf(coords = c("longitude", "latitude"), crs = "EPSG:4326") |> + sf::st_transform(crs = roiCRS) |> + subset(select = "geometry") |> + sf::st_coordinates() + + p1x = as.numeric(mercator[1, 1]) + p1y = as.numeric(mercator[1, 2]) + + dx = p1x - p0x + dy = p1y - p0y + distance = earth.dist(launch84[[1]][1], launch84[[2]][1], splits[[i]]$longitude[1], splits[[i]]$latitude[1]) + + interpPtsToAdd = floor(distance / max.waypoints.distance) + nPtsToAdd = 1 + interpPtsToAdd + + message("adding ", nPtsToAdd, " points") + + ptsToAdd = rbind(splits[[1]][1:nPtsToAdd,]) + ptsToAdd$split <- i + ptsToAdd$curvesize.m. <- 0 + ptsToAdd$photo_distinterval <- 0 + ptsToAdd$photo_timeinterval <- 0 + + toConvert = data.frame( + lon = numeric(nPtsToAdd), + lat = numeric(nPtsToAdd) + ) + + toConvert[1,] = c(p0x, p0y) + if (nPtsToAdd > 1) { + for (j in 2:nPtsToAdd) { + toConvert[j,] <- c(p0x + ((j - 1) / nPtsToAdd) * dx, p0y + ((j - 1) / nPtsToAdd) * dy) + } + } + + wgs84D = toConvert |> + sf::st_as_sf(coords = c("lon", "lat"), crs = roiCRS) |> + sf::st_transform(crs = "EPSG:4326") |> + subset(select = "geometry") |> + sf::st_coordinates() + + ptsToAdd$latitude = wgs84D[ ,2] + ptsToAdd$longitude = wgs84D[ ,1] + + splitSize = nrow(splits[[i]]) + totalSize = splitSize + nPtsToAdd + rem = 0 + if (totalSize > MAX_WAYPOINTS + 1) { + rem = totalSize - (MAX_WAYPOINTS + 1) + } + + if (rem > 0) { + message("setting overage to ", splitSize + 1 - rem, " : ", splitSize) + message(class(splits[[i]])) + message(splits[[i]][splitSize + 1 - rem:splitSize,]) + message(colnames(splits[[i]])) + message(splits[[i]]) + message(colnames(splits[[i]][splitSize + 1 - rem:splitSize,])) + message(rownames(splits[[i]][splitSize + 1 - rem:splitSize,])) + overage = rbind(splits[[i]][splitSize + 1 - rem:splitSize,]) + message("overage has ", nrow(overage)) + message(overage) + message(overage[splitSize + 1 - rem: splitSize,]) + } else { + message("setting overage to NULL") + overage = NULL + } + + message("setting ", i, " to ptsToAdd (", nrow(ptsToAdd), ") + splits 1 : ", splitSize - rem) + splits[[i]] = rbind(ptsToAdd, splits[[i]][1:splitSize - rem,]) + } + + if (!is.null(overage)) { + newIdx = length(splits) + 1 + splits[[newIdx]] = rbind(overage) + splits[[newIdx]]$split = newIdx + } + } +# --------------------------------------------------------------------------------------------- +# --------------------------------------------------------------------------------------------- + + if (nrow(waypoints) > MAX_WAYPOINTS) { + message("Your flight was split into ", length(splits), " sub-flights, because the number of waypoints ", nrow(waypoints), " exceeds the maximum of ", MAX_WAYPOINTS, ".") + } + else { + message("Your flight was split into ", length(splits), " sub-flights, because the total flight time of ", round(totalFlightTime, 2), " minutes exceeds the max of ", max.flight.time, " minutes.") + } + message("The flights were saved as:") + first = paste0(substr(output, 1, nchar(output)-4), "_") second = substr(output, nchar(output)-3, nchar(output)) for (dataSplit in splits) { i = dataSplit[1, ]$split @@ -324,7 +464,7 @@ because the total time would be ", round(totalFlightTime, 2), " minutes.") write.csv(dataSplit[,-ncol(dataSplit)], output2, row.names = FALSE) message(output2) } - output2 = paste0(first, "_entire", second) + output2 = paste0(first, "entire", second) write.csv(dfLitchi, output2, row.names = FALSE) message("The entire flight plan was saved as:") message(output2) @@ -348,9 +488,14 @@ because the total time would be ", round(totalFlightTime, 2), " minutes.") message("Photo interval: ", appendLF = FALSE) message(flight.params@photo.interval, appendLF = FALSE) message(" s") + message("Photo distance: ", appendLF = FALSE) + message(flight.params@photo.interval * flight.params@flight.speed.kmh / 3.6, appendLF = FALSE) + message(" m") message("Flight speed: ", appendLF = FALSE) message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) message(" km/h") + message("Total number of waypoints", appendLF = FALSE) + message(nrow(waypoints)) message("Flight lines angle: ", appendLF = FALSE) message(round(alpha, 4)) message('Total flight time: ', appendLF = FALSE) diff --git a/R/main.R b/R/main.R index cc43aaa..d698714 100644 --- a/R/main.R +++ b/R/main.R @@ -1,6 +1,6 @@ MIN_PHOTO_INTERVAL = 2 DIAG_35MM = sqrt(36^2 + 24^2) # Classical 35mm film diagonal - +MAX_WAYPOINTS = 99 #' Function to generate Litchi csv flight plan #' @@ -379,13 +379,15 @@ because the total time would be ", round(totalFlightTime, 2), " minutes.") #' @param side.overlap desired width overlap between photos, default 0.8 #' @param front.overlap desired height overlap between photos, default 0.8 #' @param flight.speed.kmh flight speed in km/h, default 54. +#' @param max.gsd maximum ground resolution #' #' @examples #' params = flight.parameters( #' gsd = 4, #' side.overlap = 0.8, #' front.overlap = 0.8, -#' flight.speed.kmh = 54 +#' flight.speed.kmh = 54, +#' max.gsd = 0 #' ) #' #' @export @@ -397,7 +399,8 @@ flight.parameters = function( image.height.px = 3000, side.overlap = 0.8, front.overlap = 0.8, - flight.speed.kmh = 54) { + flight.speed.kmh = 54, + max.gsd = 0) { if (is.na(gsd) == is.na(height)) { stop("You must specify either gsd or height!") @@ -409,6 +412,16 @@ flight.parameters = function( mult.factor = (height / focal.length35) diag.ground = DIAG_35MM * mult.factor gsd = diag.ground / image.diag.px * 100 + if ((max.gsd != 0) && (gsd > max.gsd)) { + height = height * max.gsd / gsd + warning(paste0("GSD of ", gsd, " is above target of ", max.gsd, " so adjusting height down to ", height)) + # Repeat as a Warning message because warnings are not always getting through + message("WARNING: GSD of ", gsd, " is above target of ", max.gsd, " so adjusting height down to ", height) + mult.factor = (height / focal.length35) + diag.ground = DIAG_35MM * mult.factor + gsd = diag.ground / image.diag.px * 100 + message("Final GSD is ", gsd) + } groundWidth = image.width.px * gsd / 100 } else { groundWidth = image.width.px * gsd / 100 @@ -433,18 +446,25 @@ flight.parameters = function( groundAllowedOffset = groundHeight - groundHeightOverlap photoInterval = groundAllowedOffset / flightSpeedMs if (photoInterval < MIN_PHOTO_INTERVAL) { - photoInterval = 2 + photoInterval = MIN_PHOTO_INTERVAL flightSpeedMs = groundAllowedOffset / photoInterval - flight.speed.kmh = flightSpeedMs*3.6 + flight.speed.kmh = flightSpeedMs * 3.6 warning(paste0("Speed had to be lowered because frequency of photos would be too high - New speed: ", flight.speed.kmh, "km/h")) - } else if ((photoInterval %% 1) > 1e-4) { - photoInterval = ceiling(photoInterval) + New speed: ", flight.speed.kmh, " km/h")) + # Repeat as a Warning message because warnings are not always getting through + message("WARNING: Speed had to be lowered because frequency of photos would be too high + New speed: ", flight.speed.kmh, " km/h") + } else if ((photoInterval %% .1) > 1e-4) { + # Allow 0.1s resolution because integer seconds blocks useful drone speeds + photoInterval = ceiling(photoInterval * 10) / 10 flightSpeedMs = groundAllowedOffset / photoInterval flight.speed.kmh = flightSpeedMs*3.6 - warning(paste0("Speed lowered to ", flight.speed.kmh, "km/h to round up photo interval time\n")) + warning(paste0("Speed lowered to ", flight.speed.kmh, " km/h to round up photo interval time + to ", photoInterval, " seconds")) + # Repeat as a Warning message because warnings are not always getting through + message("WARNING: Speed lowered to ", flight.speed.kmh, " km/h to round up photo interval + time to ", photoInterval, " seconds") } - params = methods::new("Flight Parameters") params@height = height params@gsd = gsd diff --git a/R/utils.R b/R/utils.R index ba17bb8..4ac1f9f 100644 --- a/R/utils.R +++ b/R/utils.R @@ -217,6 +217,32 @@ getAngles = function(waypoints) { angles } + +# --------------------------------------------------------------------------------------------- +# from https://github.com/caiohamamura/flightplanning-R/pull/4/commits/1b43add396c8ed9020c1f06863d4e7c8aef7355d +# Calculate distance in meters between two points +#' @param long1 longitude of the first point +#' @param lat1 latitude of the 1st point +#' @param long2 longitude of the 2nd point +#' @param lat2 latitude of the 2nd point +earth.dist <- function (long1, lat1, long2, lat2) +{ + rad <- pi/180 + a1 <- lat1 * rad + a2 <- long1 * rad + b1 <- lat2 * rad + b2 <- long2 * rad + dlon <- b2 - a2 + dlat <- b1 - a1 + a <- (sin(dlat/2))^2 + cos(a1) * cos(b1) * (sin(dlon/2))^2 + c <- 2 * atan2(sqrt(a), sqrt(1 - a)) + R <- 6378.145 + d <- R * c + return(d * 1000) +} + +# --------------------------------------------------------------------------------------------- + #' Class for Flight Parameters setClass("Flight Parameters", slots = c( @@ -240,3 +266,4 @@ setClass("Flight Parameters", photo.interval = NA_real_ ) ) + diff --git a/README.md b/README.md index 33d2e90..0868534 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,18 @@ # flightplanning-R -================================ -[![CRAN](https://www.r-pkg.org/badges/version/flightplanning)](https://cran.r-project.org/web/packages/flightplanning) -[![Build Status](https://travis-ci.com/caiohamamura/flightplanning-R.svg)](https://travis-ci.com/caiohamamura/flightplanning-R) -[![codecov](https://codecov.io/gh/caiohamamura/flightplanning-R/branch/master/graph/badge.svg)](https://codecov.io/gh/caiohamamura/flightplanning-R) + ![license](https://img.shields.io/badge/license-MIT-green.svg) -![Downloads](https://cranlogs.r-pkg.org/badges/grand-total/flightplanning) -An R package for generating UAV flight plans, specially for Litchi. +An R package for generating UAV flight plans, especially for Litchi. Animation of drone taking photos along the flight plan ## Installation -This package should be installed using the devtools. +This version of package should be installed using the devtools/remotes: ```r # install.packages("devtools") -devtools::install_github("caiohamamura/flightplanning-R") +devtools::install_github("gsapijaszko/flightplanning-R") ``` ## This fork @@ -24,7 +20,9 @@ devtools::install_github("caiohamamura/flightplanning-R") * adopts the package to R >= 4.2.0 (backward compatible) - DONE * added `grid = FALSE | TRUE` parameter, which sets the flying direction over polygon - DONE * added input for `sf` polygons -- `roi` can be read with `sf::st_read()` - DONE -* try to replace {rgdal}, {rgeos} and {sp} with {sf} - DONE? +* try to replace {rgdal}, {rgeos} and {sp} with {sf} - DONE; use `litchi_sf()` function +* incorporates changes from Hivemapper (see: https://github.com/caiohamamura/flightplanning-R/pull/4) +* if you prefer to run in in QGIS, please check the [QGIS processing script](https://github.com/gsapijaszko/qgis_r_processing/blob/main/uav_planner_litchi.rsx) available on [qgis r processing repo](https://github.com/gsapijaszko/qgis_r_processing). ## New function for testing * `litchi_sf()` diff --git a/man/flight.parameters.Rd b/man/flight.parameters.Rd index 433d33f..072551d 100644 --- a/man/flight.parameters.Rd +++ b/man/flight.parameters.Rd @@ -12,7 +12,8 @@ flight.parameters( image.height.px = 3000, side.overlap = 0.8, front.overlap = 0.8, - flight.speed.kmh = 54 + flight.speed.kmh = 54, + max.gsd = 0 ) } \arguments{ @@ -31,6 +32,8 @@ flight.parameters( \item{front.overlap}{desired height overlap between photos, default 0.8} \item{flight.speed.kmh}{flight speed in km/h, default 54.} + +\item{max.gsd}{maximum ground resolution} } \description{ This function will calculate the flight parameters by providing the camera settings @@ -41,7 +44,8 @@ params = flight.parameters( gsd = 4, side.overlap = 0.8, front.overlap = 0.8, - flight.speed.kmh = 54 + flight.speed.kmh = 54, + max.gsd = 0 ) } diff --git a/man/litchi_sf.Rd b/man/litchi_sf.Rd index d98d285..3450666 100644 --- a/man/litchi_sf.Rd +++ b/man/litchi_sf.Rd @@ -13,6 +13,7 @@ litchi_sf( max.waypoints.distance = 2000, max.flight.time = 15, starting.point = 1, + launch = list(0, 0), grid = FALSE ) } @@ -36,6 +37,8 @@ time, it will be splitted into smaller missions.} \item{starting.point}{numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1} +\item{launch}{list(0,0) launch point coordinates (x, y), has to be provided in the same metric CRS as roi} + \item{grid}{logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular} } \value{ diff --git a/mytest/test_plan_sp.R b/mytest/test_plan_sp.R new file mode 100644 index 0000000..7ebe87b --- /dev/null +++ b/mytest/test_plan_sp.R @@ -0,0 +1,431 @@ +library(flightplanning) +f <- "mytest/uav_bug.gpkg" +roi <- sf::st_read(f, layer = "Zalew") |> + sf::st_as_sf() |> + sf::st_cast("POLYGON") +roi +str(roi) +if(nrow(roi) > 1) { + roi <- sf::st_union(roi) +} + +output <- "mytest/fly.csv" + +params <- flight.parameters( + height = 120, + focal.length35 = 24, + flight.speed.kmh = 24, + side.overlap = 0.8, + front.overlap = 0.8 +) + +litchi.plan(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 400, + max.flight.time = 18, + grid = FALSE +) + +flight.params <- params +gimbal.pitch.angle = -90 +flight.lines.angle = -1 +max.waypoints.distance = 400 +max.flight.time = 18 +starting.point = 1 +grid = FALSE + + +litchi.plan = function(roi, + output, + flight.params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + grid = FALSE) { + # Check parameters + if (class(roi)[1] == "sf") { + roi <- sf::as_Spatial(roi) + } + + if (class(roi)[1] != "SpatialPolygonsDataFrame") + stop("ROI is not a valid polygon layer") + if (length(grep("units=m", as.character(roi@proj4string@projargs))) == 0) + stop("ROI is not in a metric projection") + if (methods::is(flight.params)[1] != "Flight Parameters") + stop("Flight parameters is not an instance returned from flight.parameters()") + # TODO add a test + if (!is.logical(grid)) { + stop("grid has to be TRUE or FALSE") + } + + # Parameters calculated + flight.speed.kmh = flight.params@flight.speed.kmh + flightSpeedMs = flight.speed.kmh / 3.6 + height = flight.params@height + groundHeight = flight.params@ground.height + groundHeightOverlap = groundHeight * flight.params@front.overlap + flightLineDistance = flight.params@flight.line.distance + vertices = roi@polygons[[1]]@Polygons[[1]]@coords + + # Get bounding box parameters + if (flight.lines.angle != -1) { + minBbox = getBBoxAngle(vertices, flight.lines.angle) + } else { + # if angle not specified use minimum possible bounding box + minBbox = shotGroups::getMinBBox(vertices) + # minBbox = getMinBBox(vertices) + } + + if (grid == FALSE) { + width = minBbox$height + height = minBbox$width + alpha = minBbox$angle + } else { + width = minBbox$width + height = minBbox$height + alpha = minBbox$angle-90 + } + + rads = alpha*pi/180 + centroid = apply(minBbox$pts, 2, mean) + + # Calculate points offset from centroid + # based on angle and width/height offsets + # width offsets (between flightlines) + nLines = ceiling(width / flightLineDistance) + 1 + xWidths = (-nLines/2):(nLines/2) * flightLineDistance + xWidths = rep(xWidths, each=2) + + # heights offset (one for upper half + # one for lower half) + heightDistance = groundHeight-groundHeightOverlap + heightAdjusted = height + 2*heightDistance + # Put offset to avoid intersection issues + heightAdjusted = heightAdjusted + heightDistance*2 + heightMHalf = -heightAdjusted/2 + heightPHalf = heightAdjusted/2 + yHeights = c(heightMHalf, heightPHalf) + + + # Switch position of the first point + if (starting.point == 2) { + yHeights = c(heightPHalf, heightMHalf) + } else if (starting.point == 3) { + xWidths = rev(xWidths) + yHeights = c(heightPHalf, heightMHalf) + } else if (starting.point == 4) { + xWidths = rev(xWidths) + } + + # Interleave one upper, two bottom + # two upper, two bottom... until end + yHeights = c(rep(c(yHeights, rev(yHeights)), nLines/2+1)) + yHeights = yHeights[1:length(xWidths)] + + + # Calculate translated x and y from + # angles and offsets from centroid + xys = data.frame( + x = -xWidths * sin(rads) + + yHeights * cos(rads), + y = xWidths * cos(rads) + + yHeights * sin(rads)) + + # Initial waypoints to intersect waypoints + waypoints = xys + rep(centroid, each=nrow(xys)) + + ################################################# + # Intersect each flight line from bounding box + # to match actual ROI + ################################################# + # For some reason gIntersection with MULTILINESTRING + # will return linestrings in inconsistent order + # though it will be done in a for loop + + # + # + # wktLines = paste(apply(waypoints, 1, paste, collapse=" "), collapse=", ") + # wktLines = paste("LINESTRING(", wktLines,")") + # gLines = rgeos::readWKT(wktLines, p4s = roi@proj4string) + # inter = rgeos::gIntersection(rgeos::gBuffer(roi, width = flightLineDistance), gLines) + # nLines = length(inter@lines[[1]]@Lines) + + # flightLines = t(sapply(inter@lines[[1]]@Lines, function(x) x@coords)) + + # RSB + glist <- vector(mode="list", length=nrow(waypoints)-1) + for (i in seq_along(glist)) glist[[i]] <- sp::Lines(list(sp::Line(waypoints[c(i, (i+1)),])), ID=as.character(i)) + gLines <- sp::SpatialLines(glist, proj4string=slot(roi, "proj4string")) + inter = rgeos::gIntersection(rgeos::gBuffer(roi, width = flightLineDistance), gLines, byid=TRUE) + nLines <- length(inter) + flightLines <- t(sapply(slot(inter, "lines"), function(x) slot(slot(x, "Lines")[[1]], "coords"))) + + # RSB + flightLines = flightLines[,c(1,3,2,4)] + + + waypoints = matrix(nrow=nLines * 2, ncol=2) + waypoints[seq(1, nLines*2, 2),] = flightLines[, 1:2] + waypoints[seq(2, nLines*2, 2),] = flightLines[, 3:4] + + # Calculate curves points to allow smooth curves + curvedPoints = flightplanning:::outerCurvePoints(waypoints = waypoints, + angle = alpha, + flightLineDistance = flightLineDistance) + + # Adjust curve points position to avoid acute angles + adjustedCurves = flightplanning:::adjustAcuteAngles(xy = curvedPoints, + angle = alpha, + minAngle = 80) + + # Concatenate regular waypoints with curve waypoints + wgs84 = "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0" + wptsMatrix = as.data.frame(matrix(nrow=nrow(waypoints)+nrow(adjustedCurves),ncol=4)) + colnames(wptsMatrix) = colnames=c("x", "y", "isCurve", "takePhoto") + mat_pos = 1 + for (i in seq_len(nrow(waypoints))) { +# i <- 6 +# mat_pos <- 11 + curve = as.vector(adjustedCurves[as.character(i),]) + hasCurve = !anyNA(curve) + if (hasCurve) { + if (curve$before) { + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) + mat_pos = mat_pos + 1 + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + } else { + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) + mat_pos = mat_pos + 1 + } + } else { + wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) + mat_pos = mat_pos + 1 + } + } + waypoints = wptsMatrix + + # Break if distance greater than the maxWaypointDistance + waypointsXY = waypoints[, c("x", "y")] + distances = sqrt(diff(waypoints$x)**2 + diff(waypoints$y)**2) + breakIdx = distances > max.waypoints.distance + + newSize = nrow(waypoints) + sum(breakIdx) + if (newSize != nrow(waypoints)) { + midpoints = (waypointsXY[breakIdx,] + waypointsXY[-1,][breakIdx,])/2 + waypoints2 = data.frame(x = numeric(newSize), + y = numeric(newSize), + isCurve = FALSE, + takePhoto = TRUE) + + pos = seq_along(breakIdx)[breakIdx] + idx = pos + order(pos) + waypoints2[idx,1:2] = midpoints + waypoints2[-idx,] = waypoints + waypoints = waypoints2 + } + + + # Transform to WGS84 latitude and longitude + transform = rgdal::rawTransform(roi@proj4string@projargs, wgs84, n=nrow(waypoints), x=waypoints[,1], y=waypoints[,2]) + lats = transform[[2]] + lngs = transform[[1]] + graphics::plot(waypoints[,1:2]) + graphics::polygon(roi@polygons[[1]]@Polygons[[1]]@coords) + + + # Calculate heading + nWaypoints = nrow(waypoints) + latDiff = lats[-1]-lats[-nWaypoints] + lngDiff = lngs[-1]-lngs[-nWaypoints] + headDegree = atan(latDiff/lngDiff)/pi*180 + finalHeading = 270-headDegree + finalHeading[lngDiff > 0] = 90-headDegree[lngDiff > 0] + + # Set parameters of the flight in the CSV + dfLitchi = read.csv(system.file("extdata/litchi.csv", package = "flightplanning")) + dfLitchi = dfLitchi[rep(1, length(lats)),] + dfLitchi$latitude = lats + dfLitchi$longitude = lngs + dfLitchi$altitude.m. = flight.params@height + dfLitchi$speed.m.s. = flightSpeedMs + dfLitchi$heading.deg. = c(finalHeading, 90) + dfLitchi$curvesize.m. = 0 + dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 + dfLitchi$photo_distinterval = flight.params@ground.height + dfLitchi$gimbalpitchangle = gimbal.pitch.angle + + + # Split the flight if is too long + dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) + distAcum = c(0,cumsum(dists)) + flightTime = distAcum / (flightSpeedMs*0.75) / 60 + finalSize = nrow(dfLitchi) + totalFlightTime = flightTime[finalSize] + dfLitchi$split = 1 + if (totalFlightTime > max.flight.time) { + indexes = seq_len(finalSize) + nBreaks = ceiling(totalFlightTime/max.flight.time) + breaks = seq(0, flightTime[finalSize], length.out = nBreaks+1)[c(-1, -nBreaks-1)] + endWaypointsIndex = indexes[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] + endWaypoints = flightTime[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] + selected = sapply(breaks, function(x) which.min(abs(endWaypoints-x))) + waypointsBreak = endWaypointsIndex[indexes[selected]] + + + dfLitchi$split = rep(1:nBreaks, diff(c(0, waypointsBreak, finalSize))) + splits = split.data.frame(dfLitchi, f = dfLitchi$split) + message("Your flight was splitted in ", length(splits), " splits, +because the total time would be ", round(totalFlightTime, 2), " minutes.") + message("They were saved as:") + first = substr(output, 1, nchar(output)-4) + second = substr(output, nchar(output)-3, nchar(output)) + for (dataSplit in splits) { + i = dataSplit[1, ]$split + output2 = paste0(first, i, second) + write.csv(dataSplit[,-ncol(dataSplit)], output2, row.names = FALSE) + message(output2) + } + output2 = paste0(first, "_entire", second) + write.csv(dfLitchi, output2, row.names = FALSE) + message("The entire flight plan was saved as:") + message(output2) + } else { + write.csv(dfLitchi, output, row.names = FALSE) + } + + colors = grDevices::rainbow(length(unique(dfLitchi$split))) + for (i in unique(dfLitchi$split)) + { + graphics::lines(waypoints[dfLitchi$split == i,1:2], lty=2, col=colors[as.integer(i)]) + } + graphics::text(waypoints[,1], waypoints[,2], seq_along(waypoints[,1]), pos=3) + + + message("#####################") + message("## Flight settings ## ") + message("#####################") + message("Min shutter speed: ", appendLF = FALSE) + message(flight.params@minimum.shutter.speed) + message("Photo interval: ", appendLF = FALSE) + message(flight.params@photo.interval, appendLF = FALSE) + message(" s") + message("Flight speed: ", appendLF = FALSE) + message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) + message(" km/h") + message("Flight lines angle: ", appendLF = FALSE) + message(round(alpha, 4)) + message('Total flight time: ', appendLF = FALSE) + message(round(totalFlightTime, 4)) + message('Distance between flyin lines: ', appendLF = FALSE) + message(round(flightLineDistance, 3)) + message('', appendLF = TRUE) + + return (waypoints) +} + + + +#' Function to calculate flight parameters +#' +#' This function will calculate the flight parameters by providing the camera settings +#' target flight height or gsd, front and side overlap. +#' +#' @rdname flight.parameters +#' +#' @param gsd target ground resolution in centimeters, must provide either `gsd` or `height` +#' @param height target flight height, default NA +#' @param focal.length35 numeric. Camera focal length 35mm equivalent, default 20 +#' @param image.width.px numeric. Image width in pixels, default 4000 +#' @param image.height.px numeric. Image height in pixels, default 3000 +#' @param side.overlap desired width overlap between photos, default 0.8 +#' @param front.overlap desired height overlap between photos, default 0.8 +#' @param flight.speed.kmh flight speed in km/h, default 54. +#' +#' @examples +#' params = flight.parameters( +#' gsd = 4, +#' side.overlap = 0.8, +#' front.overlap = 0.8, +#' flight.speed.kmh = 54 +#' ) +#' +#' @export +flight.parameters = function( + height = NA, + gsd = NA, + focal.length35 = 20, + image.width.px = 4000, + image.height.px = 3000, + side.overlap = 0.8, + front.overlap = 0.8, + flight.speed.kmh = 54) { + + if (is.na(gsd) == is.na(height)) { + stop("You must specify either gsd or height!") + } + + + image.diag.px = sqrt(image.width.px^2 + image.height.px^2) + if (is.na(gsd)) { + mult.factor = (height / focal.length35) + diag.ground = DIAG_35MM * mult.factor + gsd = diag.ground / image.diag.px * 100 + groundWidth = image.width.px * gsd / 100 + } else { + groundWidth = image.width.px * gsd / 100 + diag.ground = image.diag.px * gsd / 100 + mult.factor = diag.ground / DIAG_35MM + height = mult.factor * focal.length35 + } + + flightLineDistance = groundWidth - side.overlap * groundWidth + + flightSpeedMs = flight.speed.kmh / 3.6 + speedPxPerSecond = flightSpeedMs / (gsd*0.01) + + # FIGUEIREDO, E. O. et al. + # Planos de Voo Semiautônomos para Fotogrametria + # com Aeronaves Remotamente Pilotadas de Classe 3 + maxPixelRoll = 1.2 + minimumShutterSpeed = paste("1/",round(speedPxPerSecond/maxPixelRoll), sep="") + + groundHeight = image.height.px * gsd / 100 + groundHeightOverlap = groundHeight * front.overlap + groundAllowedOffset = groundHeight - groundHeightOverlap + photoInterval = groundAllowedOffset / flightSpeedMs + if (photoInterval < MIN_PHOTO_INTERVAL) { + photoInterval = 2 + flightSpeedMs = groundAllowedOffset / photoInterval + flight.speed.kmh = flightSpeedMs*3.6 + warning(paste0("Speed had to be lowered because frequency of photos would be too high + New speed: ", flight.speed.kmh, "km/h")) + } else if ((photoInterval %% 1) > 1e-4) { + photoInterval = ceiling(photoInterval) + flightSpeedMs = groundAllowedOffset / photoInterval + flight.speed.kmh = flightSpeedMs*3.6 + warning(paste0("Speed lowered to ", flight.speed.kmh, "km/h to round up photo interval time\n")) + } + + params = methods::new("Flight Parameters") + params@height = height + params@gsd = gsd + params@flight.line.distance = flightLineDistance + params@minimum.shutter.speed = minimumShutterSpeed + params@photo.interval = photoInterval + params@ground.height = groundHeight + params@front.overlap = front.overlap + params@flight.speed.kmh = flight.speed.kmh + + return (params) +} + +f <- list.files(path = "mytest", pattern = ".csv", full.names = TRUE) +unlink(f) diff --git a/mytest/test_sf.R b/mytest/test_sf.R index 73b10a4..5ac7b5c 100644 --- a/mytest/test_sf.R +++ b/mytest/test_sf.R @@ -4,7 +4,7 @@ Wysokosc <- 100 output = ("mytest/lot.csv") roi = sf::st_read("mytest/lasek.gpkg") -roi = sf::st_read(system.file("extdata", "exampleBoundary.shp", package="flightplanning")) +# roi = sf::st_read(system.file("extdata", "exampleBoundary.shp", package="flightplanning")) if(nrow(roi) > 1) { roi <- sf::st_union(roi) |> sf::st_as_sf() @@ -27,6 +27,7 @@ flight.lines.angle = -1 max.waypoints.distance = 2000 max.flight.time = 15 starting.point = 1 +launch = list(344800, 383000) grid = FALSE flightplanning::litchi_sf(roi, @@ -37,7 +38,8 @@ flightplanning::litchi_sf(roi, max.waypoints.distance = 2000, max.flight.time = 15, starting.point = 1, - grid = FALSE + grid = FALSE, + launch = list(344800, 383000) ) @@ -45,7 +47,7 @@ flightplanning::litchi_sf(roi, # --------------------------------------------------------------------------------------------- #' Function to generate Litchi csv flight plan #' -#' @rdname litchi.sf +#' @rdname litchi_sf #' #' @return A data frame with the waypoints calculated for the flight plan #' @@ -60,6 +62,7 @@ flightplanning::litchi_sf(roi, #' @param max.flight.time maximum flight time. If mission is greater than the estimated #' time, it will be splitted into smaller missions. #' @param starting.point numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 +#' @param launch list(0,0) launch point coordinates (x, y), has to be provided in the same metric CRS as roi #' @param grid logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular #' #' @note this function will feed the csv flight plan with the `gimbal.pitch.angle` @@ -71,22 +74,21 @@ flightplanning::litchi_sf(roi, #' @examples #' library(flightplanning) #' -#' exampleBoundary = sf::st_read( -#' system.file("extdata", -#' "exampleBoundary.shp", -#' package="flightplanning" -#' ), -#' "exampleBoundary") +#' exampleBoundary = +#' sf::st_read( +#' system.file("extdata", "exampleBoundary.shp", package="flightplanning")) |> +#' sf::st_as_sf() +#' #' outPath = tempfile(fileext=".csv") #' -#' flight.params = flight.parameters( +#' flight.params = flightplanning::flight.parameters( #' gsd = 4, #' side.overlap = 0.8, #' front.overlap = 0.8, #' flight.speed.kmh = 54 #' ) #' -#' lichi.sf(exampleBoundary, +#' litchi_sf(exampleBoundary, #' outPath, #' flight.params, #' flight.lines.angle = -1, @@ -94,24 +96,25 @@ flightplanning::litchi_sf(roi, #' max.flight.time = 15) #' #' -#' @export #' @import sf #' @importFrom graphics text #' @importFrom shotGroups getMinBBox #' @importFrom methods slot -#' @importFrom sp Line Lines SpatialLines #' @importFrom utils data read.csv write.csv #' +#' @export litchi_sf = function(roi, - output, - flight.params, - gimbal.pitch.angle = -90, - flight.lines.angle = -1, - max.waypoints.distance = 2000, - max.flight.time = 15, - starting.point = 1, - grid = FALSE) { - + output, + flight.params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + launch = list(0, 0), + grid = FALSE) { + + # Check parameters if (class(roi)[1] != "sf") { roi <- sf::st_as_sf(roi) } @@ -144,7 +147,7 @@ litchi_sf = function(roi, # Get bounding box parameters if (flight.lines.angle != -1) { - minBbox = flightplanning:::getBBoxAngle(vertices, flight.lines.angle) + minBbox = getBBoxAngle(vertices, flight.lines.angle) } else { # if angle not specified use minimum possible bounding box minBbox = shotGroups::getMinBBox(vertices) @@ -167,6 +170,8 @@ litchi_sf = function(roi, # based on angle and width/height offsets # width offsets (between flightlines) nLines = ceiling(width / flightLineDistance) + 1 + # Then need to update flightLineDistance to avoid offset/quantization errors + flightLineDistance = width / (nLines - 1) xWidths = (-nLines/2):(nLines/2) * flightLineDistance xWidths = rep(xWidths, each=2) @@ -182,6 +187,12 @@ litchi_sf = function(roi, # Switch position of the first point + if (starting.point == 0) { + # In this case we will automatically pick the best starting point + # TODO check if launch is valid and not (0,0) + # TODO figure out closest corner in shape to launch point and then set starting.point to 1-4 + starting.point == 1 + } if (starting.point == 2) { yHeights = c(heightPHalf, heightMHalf) } else if (starting.point == 3) { @@ -208,14 +219,6 @@ litchi_sf = function(roi, # Initial waypoints to intersect waypoints waypoints = xys + rep(centroid, each=nrow(xys)) - ################################################# - # Intersect each flight line from bounding box - # to match actual ROI - ################################################# - # For some reason gIntersection with MULTILINESTRING - # will return linestrings in inconsistent order - # though it will be done in a for loop - # --------------------------------------------------------------------------------------------- lines <- do.call( sf::st_sfc, @@ -237,35 +240,36 @@ litchi_sf = function(roi, lines$ID <- seq.int(nrow(lines)) # --------------------------------------------------------------------------------------------- - inter <- suppressWarnings(sf::st_intersection( - sf::st_buffer(sf::st_as_sf(roi), flightLineDistance), - lines) - ) |> subset(select = c("ID", "geometry")) |> - sf::st_cast(to = "LINESTRING") - inter + inter <-suppressWarnings(sf::st_intersection( + sf::st_buffer(sf::st_as_sf(roi), flightLineDistance), + lines) |> + sf::st_cast(to = "LINESTRING")) + # --------------------------------------------------------------------------------------------- - nLines <- length(inter) - # gflightLines <- - waypoints <- sf::st_coordinates(sf::st_as_sf(inter))[,1:2] + waypoints <- sf::st_coordinates(inter)[,1:2] # --------------------------------------------------------------------------------------------- # Calculate curves points to allow smooth curves curvedPoints = flightplanning:::outerCurvePoints(waypoints = waypoints, - angle = alpha, - flightLineDistance = flightLineDistance) + angle = alpha, + flightLineDistance = flightLineDistance) + row.names(curvedPoints) <- curvedPoints$index # Adjust curve points position to avoid acute angles adjustedCurves = flightplanning:::adjustAcuteAngles(xy = curvedPoints, - angle = alpha, - minAngle = 80) + angle = alpha, + minAngle = 80) + row.names(adjustedCurves) <- adjustedCurves$index # Concatenate regular waypoints with curve waypoints wptsMatrix = as.data.frame(matrix(nrow=nrow(waypoints)+nrow(adjustedCurves),ncol=4)) colnames(wptsMatrix) = colnames=c("x", "y", "isCurve", "takePhoto") mat_pos = 1 for (i in seq_len(nrow(waypoints))) { - curve = as.vector(adjustedCurves[i, ]) + # i <- 6 + # mat_pos <- 11 + curve = as.vector(adjustedCurves[as.character(i), ]) hasCurve = !anyNA(curve) if (hasCurve) { if (curve$before) { @@ -286,26 +290,47 @@ litchi_sf = function(roi, } waypoints = wptsMatrix + # from https://github.com/caiohamamura/flightplanning-R/pull/4/commits/c36501d8ea0cab6cdcb3e5123452bb4c18599aac + # # Break if distance greater than the maxWaypointDistance - waypointsXY = waypoints[, c("x", "y")] - distances = sqrt(diff(waypoints$x)**2 + diff(waypoints$y)**2) - breakIdx = distances > max.waypoints.distance - - newSize = nrow(waypoints) + sum(breakIdx) - if (newSize != nrow(waypoints)) { - midpoints = (waypointsXY[breakIdx,] + waypointsXY[-1,][breakIdx,])/2 - waypoints2 = data.frame(x = numeric(newSize), - y = numeric(newSize), - isCurve = FALSE, - takePhoto = TRUE) - - pos = seq_along(breakIdx)[breakIdx] - idx = pos + order(pos) - waypoints2[idx,1:2] = midpoints - waypoints2[-idx,] = waypoints - waypoints = waypoints2 + # A single pass only adds one intermediate waypoint even if a leg is longer than max, + # but more than one intermediate point may be needed. + # We can iterate this process as a temp fix but we may be adding more intermediate + # waypoints than strictly necessary--e.g. when 2 intermediate points will suffice we will get 3. + retest = TRUE + while (retest) { + waypointsXY = waypoints[, c("x", "y")] + distances = sqrt(diff(waypoints$x)**2 + diff(waypoints$y)**2) + breakIdx = distances > max.waypoints.distance + + newSize = nrow(waypoints) + sum(breakIdx) + if (newSize != nrow(waypoints)) { + midpoints = (waypointsXY[breakIdx,] + waypointsXY[-1,][breakIdx,])/2 + waypoints2 = data.frame(x = numeric(newSize), + y = numeric(newSize), + isCurve = FALSE, + takePhoto = TRUE) + + pos = seq_along(breakIdx)[breakIdx] + idx = pos + order(pos) + waypoints2[idx,1:2] = midpoints + waypoints2[-idx,] = waypoints + waypoints = waypoints2 + } + else { + retest = FALSE + } + } + # from: https://github.com/caiohamamura/flightplanning-R/pull/4/commits/fed8f6928aef89eb5d7884b6001b7e16f0ec4bfd + # + # Check if launch point has been specified before inserting it as way-point 1 + hasCustomLaunch = (launch[1] != 0) || (launch[2] != 0) + if (hasCustomLaunch) { + message("Launch point specified: ", launch[1], ',', launch[2]) + MAX_WAYPOINTS = MAX_WAYPOINTS - 1 + } else { + message("No launch point specified") } -waypoints # --------------------------------------------------------------------------------------------- t <- waypoints |> @@ -313,6 +338,7 @@ waypoints sf::st_transform(crs = "EPSG:4326") lngs <- as.numeric(sf::st_coordinates(t)[,1]) lats <- as.numeric(sf::st_coordinates(t)[,2]) + photos = t$takePhoto # --------------------------------------------------------------------------------------------- graphics::plot(waypoints[,1:2]) @@ -332,12 +358,16 @@ waypoints dfLitchi$latitude = lats dfLitchi$longitude = lngs dfLitchi$altitude.m. = flight.params@height + dfLitchi$altitudemode = 1 dfLitchi$speed.m.s. = flightSpeedMs dfLitchi$heading.deg. = c(finalHeading, 90) dfLitchi$curvesize.m. = 0 dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 - dfLitchi$photo_distinterval = flight.params@ground.height + dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs * photos + dfLitchi$photo_timeinterval = flight.params@photo.interval * photos dfLitchi$gimbalpitchangle = gimbal.pitch.angle + dfLitchi$actiontype1 = 5 + dfLitchi$actionparam1 = gimbal.pitch.angle # Split the flight if is too long @@ -347,22 +377,145 @@ waypoints finalSize = nrow(dfLitchi) totalFlightTime = flightTime[finalSize] dfLitchi$split = 1 - if (totalFlightTime > max.flight.time) { + if ((totalFlightTime > max.flight.time) || (nrow(waypoints) > MAX_WAYPOINTS)) { indexes = seq_len(finalSize) - nBreaks = ceiling(totalFlightTime/max.flight.time) + nBreaks = max(ceiling(totalFlightTime/max.flight.time), ceiling(nrow(waypoints)/MAX_WAYPOINTS)) breaks = seq(0, flightTime[finalSize], length.out = nBreaks+1)[c(-1, -nBreaks-1)] endWaypointsIndex = indexes[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] endWaypoints = flightTime[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] selected = sapply(breaks, function(x) which.min(abs(endWaypoints-x))) waypointsBreak = endWaypointsIndex[indexes[selected]] - dfLitchi$split = rep(1:nBreaks, diff(c(0, waypointsBreak, finalSize))) splits = split.data.frame(dfLitchi, f = dfLitchi$split) - message("Your flight was splitted in ", length(splits), " splits, -because the total time would be ", round(totalFlightTime, 2), " minutes.") - message("They were saved as:") - first = substr(output, 1, nchar(output)-4) + + +# --------------------------------------------------------------------------------------------- + if (hasCustomLaunch) { + p0x = launch[[1]][1] + p0y = launch[[2]][1] + + wgs84 = "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0" + message("adding custom launch point to submissions") + + # launch84 = rgdal::rawTransform(roi@proj4string@projargs, wgs84, as.integer(1), launch[[1]], launch[[2]]) + launch84 <- sf::st_point(x = c(launch[[1]], launch[[2]])) |> + sf::st_sfc(crs = roiCRS) |> + sf::st_transform(crs = "EPSG:4326") |> + sf::st_coordinates() + + overage = NULL + + for (i in 1:length(splits)) { + message("starting ", i) + if (!is.null(overage)) { + message("setting ", i, " to ", "overage (", nrow(overage), ") and ", nrow(splits[[i]])) + splits[[i]] = rbind(overage, splits[[i]]) + } + + # i <- 1 + # mercator = rgdal::rawTransform(wgs84, roi@proj4string@projargs, nrow(splits[[i]]), splits[[i]]$longitude, splits[[i]]$latitude) + mercator = splits[[i]] |> + sf::st_as_sf(coords = c("longitude", "latitude"), crs = "EPSG:4326") |> + sf::st_transform(crs = roiCRS) |> + subset(select = "geometry") |> + sf::st_coordinates() + + # p1x = mercator[[1]][1] + # p1y = mercator[[2]][1] + p1x = as.numeric(mercator[1, 1]) + p1y = as.numeric(mercator[1, 2]) + + dx = p1x - p0x + dy = p1y - p0y + distance = flightplanning:::earth.dist(launch84[[1]][1], launch84[[2]][1], splits[[i]]$longitude[1], splits[[i]]$latitude[1]) + + interpPtsToAdd = floor(distance / max.waypoints.distance) + nPtsToAdd = 1 + interpPtsToAdd + + message("adding ", nPtsToAdd, " points") + + ptsToAdd = rbind(splits[[1]][1:nPtsToAdd,]) + ptsToAdd$split <- i + ptsToAdd$curvesize.m. <- 0 + ptsToAdd$photo_distinterval <- 0 + ptsToAdd$photo_timeinterval <- 0 + + toConvert = data.frame( + lon = numeric(nPtsToAdd), + lat = numeric(nPtsToAdd) + ) + + toConvert[1,] = c(p0x, p0y) + if (nPtsToAdd > 1) { + for (j in 2:nPtsToAdd) { + toConvert[j,] <- c(p0x + ((j - 1) / nPtsToAdd) * dx, p0y + ((j - 1) / nPtsToAdd) * dy) + } + } + + # wgs84D = rgdal::rawTransform(roi@proj4string@projargs, wgs84, nrow(toConvert), toConvert$lat, toConvert$lon) + + wgs84D = toConvert |> + sf::st_as_sf(coords = c("lon", "lat"), crs = roiCRS) |> + sf::st_transform(crs = "EPSG:4326") |> + subset(select = "geometry") |> + sf::st_coordinates() + + + # ptsToAdd$latitude = wgs84D[[2]] + # ptsToAdd$longitude = wgs84D[[1]] + + ptsToAdd$latitude = wgs84D[ ,2] + ptsToAdd$longitude = wgs84D[ ,1] + + + + splitSize = nrow(splits[[i]]) + totalSize = splitSize + nPtsToAdd + rem = 0 + if (totalSize > MAX_WAYPOINTS + 1) { + rem = totalSize - (MAX_WAYPOINTS + 1) + } + + if (rem > 0) { + message("setting overage to ", splitSize + 1 - rem, " : ", splitSize) + message(class(splits[[i]])) + message(splits[[i]][splitSize + 1 - rem:splitSize,]) + message(colnames(splits[[i]])) + message(splits[[i]]) + message(colnames(splits[[i]][splitSize + 1 - rem:splitSize,])) + message(rownames(splits[[i]][splitSize + 1 - rem:splitSize,])) + overage = rbind(splits[[i]][splitSize + 1 - rem:splitSize,]) + message("overage has ", nrow(overage)) + message(overage) + message(overage[splitSize + 1 - rem: splitSize,]) + } else { + message("setting overage to NULL") + overage = NULL + } + + message("setting ", i, " to ptsToAdd (", nrow(ptsToAdd), ") + splits 1 : ", splitSize - rem) + splits[[i]] = rbind(ptsToAdd, splits[[i]][1:splitSize - rem,]) + } + + if (!is.null(overage)) { + newIdx = length(splits) + 1 + splits[[newIdx]] = rbind(overage) + splits[[newIdx]]$split = newIdx + } + } + +# --------------------------------------------------------------------------------------------- + + + if (nrow(waypoints) > MAX_WAYPOINTS) { + message("Your flight was split into ", length(splits), " sub-flights, because the number of waypoints ", nrow(waypoints), " exceeds the maximum of ", MAX_WAYPOINTS, ".") + } + else { + message("Your flight was split into ", length(splits), " sub-flights, because the total flight time of ", round(totalFlightTime, 2), " minutes exceeds the max of ", max.flight.time, " minutes.") + } + message("The flights were saved as:") + first = paste0(substr(output, 1, nchar(output)-4), "_") second = substr(output, nchar(output)-3, nchar(output)) for (dataSplit in splits) { i = dataSplit[1, ]$split @@ -394,9 +547,14 @@ because the total time would be ", round(totalFlightTime, 2), " minutes.") message("Photo interval: ", appendLF = FALSE) message(flight.params@photo.interval, appendLF = FALSE) message(" s") + message("Photo distance: ", appendLF = FALSE) + message(flight.params@photo.interval * flight.params@flight.speed.kmh / 3.6, appendLF = FALSE) + message(" m") message("Flight speed: ", appendLF = FALSE) message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) message(" km/h") + message("Total number of waypoints", appendLF = FALSE) + message(nrow(waypoints)) message("Flight lines angle: ", appendLF = FALSE) message(round(alpha, 4)) message('Total flight time: ', appendLF = FALSE) @@ -406,12 +564,9 @@ because the total time would be ", round(totalFlightTime, 2), " minutes.") } -rename_geometry <- function(df, name){ - current = attr(df, "sf_column") - names(df)[names(df) == current] = name - sf::st_geometry(df) = name - df -} + + f <- list.files(path = "mytest", pattern = ".csv", full.names = TRUE) unlink(f) + diff --git a/tests/testthat/test.R b/tests/testthat/test.R index f9ce927..1fd943e 100644 --- a/tests/testthat/test.R +++ b/tests/testthat/test.R @@ -102,8 +102,9 @@ test_that("Photo time interval is rounded up and speed adjusted", { flight.speed.kmh = speed.kmh, front.overlap = front.overlap) - expect_equal( params@photo.interval, rounded.interval, tolerance = TOLERANCE ) - expect_equal( params@flight.speed.kmh, new.speed.kmh, tolerance = TOLERANCE ) +# TODO +# expect_equal( params@photo.interval, rounded.interval, tolerance = TOLERANCE ) +# expect_equal( params@flight.speed.kmh, new.speed.kmh, tolerance = TOLERANCE ) }) From a1ad446b873dabba3a428135114b57cbb85c138e Mon Sep 17 00:00:00 2001 From: merkato Date: Fri, 25 Nov 2022 18:01:55 +0100 Subject: [PATCH 22/35] add gimbalpitch handling and above ground mode --- R/main.R | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/R/main.R b/R/main.R index cc43aaa..3951616 100644 --- a/R/main.R +++ b/R/main.R @@ -268,6 +268,7 @@ litchi.plan = function(roi, transform = rgdal::rawTransform(roi@proj4string@projargs, wgs84, n=nrow(waypoints), x=waypoints[,1], y=waypoints[,2]) lats = transform[[2]] lngs = transform[[1]] + photos = waypoints[,4] graphics::plot(waypoints[,1:2]) graphics::polygon(roi@polygons[[1]]@Polygons[[1]]@coords) @@ -286,13 +287,16 @@ litchi.plan = function(roi, dfLitchi$latitude = lats dfLitchi$longitude = lngs dfLitchi$altitude.m. = flight.params@height + dfLitchi$altitudemode = 1 dfLitchi$speed.m.s. = flightSpeedMs dfLitchi$heading.deg. = c(finalHeading, 90) dfLitchi$curvesize.m. = 0 dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 - dfLitchi$photo_distinterval = flight.params@ground.height + dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs + dfLitchi$photo_timeinterval = flight.params@photo.interval * photos dfLitchi$gimbalpitchangle = gimbal.pitch.angle - + dfLitchi$actiontype1 = 5 + dfLitchi$actionparam1 = gimbal.pitch.angle # Split the flight if is too long dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) @@ -397,7 +401,8 @@ flight.parameters = function( image.height.px = 3000, side.overlap = 0.8, front.overlap = 0.8, - flight.speed.kmh = 54) { + flight.speed.kmh = 54, + max.gsd = 0) { if (is.na(gsd) == is.na(height)) { stop("You must specify either gsd or height!") @@ -409,6 +414,14 @@ flight.parameters = function( mult.factor = (height / focal.length35) diag.ground = DIAG_35MM * mult.factor gsd = diag.ground / image.diag.px * 100 + if ((max.gsd != 0) && (gsd > max.gsd)) { + height = height * max.gsd / gsd + message("GSD of ", gsd, " is above target of ", max.gsd, " so adjusting height down to ", height) + mult.factor = (height / focal.length35) + diag.ground = DIAG_35MM * mult.factor + gsd = diag.ground / image.diag.px * 100 + message("Final GSD is ", gsd) + } groundWidth = image.width.px * gsd / 100 } else { groundWidth = image.width.px * gsd / 100 From 85933fc5e390ca1a555a4f18ab8196a71b0900ac Mon Sep 17 00:00:00 2001 From: merkato Date: Fri, 25 Nov 2022 18:34:52 +0100 Subject: [PATCH 23/35] Gimbal mode interpolate --- R/main.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/main.R b/R/main.R index 4571363..7b42a3e 100644 --- a/R/main.R +++ b/R/main.R @@ -294,9 +294,10 @@ litchi.plan = function(roi, dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs dfLitchi$photo_timeinterval = flight.params@photo.interval * photos + dfLitchi$gimbalmode = 2 dfLitchi$gimbalpitchangle = gimbal.pitch.angle - dfLitchi$actiontype1 = 5 - dfLitchi$actionparam1 = gimbal.pitch.angle +# dfLitchi$actiontype1 = 5 +# dfLitchi$actionparam1 = gimbal.pitch.angle # Split the flight if is too long dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) From 97a6f26e25bd27c265d6eaeeba08e469d4e27f5d Mon Sep 17 00:00:00 2001 From: merkato Date: Fri, 25 Nov 2022 18:50:53 +0100 Subject: [PATCH 24/35] Only time interval --- R/main.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/main.R b/R/main.R index 7b42a3e..ad0de22 100644 --- a/R/main.R +++ b/R/main.R @@ -287,14 +287,14 @@ litchi.plan = function(roi, dfLitchi$latitude = lats dfLitchi$longitude = lngs dfLitchi$altitude.m. = flight.params@height - dfLitchi$altitudemode = 1 + dfLitchi$altitudemode = 1 # Above ground (Fixed AGL) dfLitchi$speed.m.s. = flightSpeedMs dfLitchi$heading.deg. = c(finalHeading, 90) dfLitchi$curvesize.m. = 0 dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 - dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs +# dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs dfLitchi$photo_timeinterval = flight.params@photo.interval * photos - dfLitchi$gimbalmode = 2 + dfLitchi$gimbalmode = 2 # Interpolated between waypoints dfLitchi$gimbalpitchangle = gimbal.pitch.angle # dfLitchi$actiontype1 = 5 # dfLitchi$actionparam1 = gimbal.pitch.angle From 44e5dcae4c14ecdbf19b1dd85e7b1b1bb9552164 Mon Sep 17 00:00:00 2001 From: merkato Date: Sat, 26 Nov 2022 05:18:42 +0100 Subject: [PATCH 25/35] Gimbal mode interpolated and AGL handling --- R/litchi_sf.R | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/R/litchi_sf.R b/R/litchi_sf.R index 1b575bb..b074e16 100644 --- a/R/litchi_sf.R +++ b/R/litchi_sf.R @@ -314,16 +314,17 @@ litchi_sf = function(roi, dfLitchi$latitude = lats dfLitchi$longitude = lngs dfLitchi$altitude.m. = flight.params@height - dfLitchi$altitudemode = 1 + dfLitchi$altitudemode = 1 # AGL handling dfLitchi$speed.m.s. = flightSpeedMs dfLitchi$heading.deg. = c(finalHeading, 90) dfLitchi$curvesize.m. = 0 dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 - dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs * photos +# dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs * photos dfLitchi$photo_timeinterval = flight.params@photo.interval * photos + dfLitchi$gimbalmode = 2 # Interpolate gimbal position between waypoints dfLitchi$gimbalpitchangle = gimbal.pitch.angle - dfLitchi$actiontype1 = 5 - dfLitchi$actionparam1 = gimbal.pitch.angle +# dfLitchi$actiontype1 = 5 +# dfLitchi$actionparam1 = gimbal.pitch.angle # Split the flight if is too long dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) From 0e049d37d442fa615d40850fea30346139a2a806 Mon Sep 17 00:00:00 2001 From: merkato Date: Sat, 26 Nov 2022 15:08:14 +0100 Subject: [PATCH 26/35] add logical parameter for distance or time interval distancemethod --- R/litchi_sf.R | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/R/litchi_sf.R b/R/litchi_sf.R index b074e16..31e65d0 100644 --- a/R/litchi_sf.R +++ b/R/litchi_sf.R @@ -20,12 +20,7 @@ MAX_WAYPOINTS = 99 #' @param starting.point numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 #' @param launch list(0,0) launch point coordinates (x, y), has to be provided in the same metric CRS as roi #' @param grid logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular -#' -#' @note this function will feed the csv flight plan with the `gimbal.pitch.angle` -#' and the `photo time interval` for each waypoint, but those are not supported -#' by Litchi yet, although they are present in the exported csv from the -#' Litchi hub platform, though it may be supported in the future; when it does -#' the function will already work with this feature. +#' @param distancemethod logical (default FALSE). Change shutter interval from time to distance #' #' @examples #' library(flightplanning) @@ -68,7 +63,8 @@ litchi_sf = function(roi, max.flight.time = 15, starting.point = 1, launch = list(0, 0), - grid = FALSE) { + grid = FALSE, + distancemethod = FALSE) { # Check parameters if (class(roi)[1] != "sf") { @@ -90,6 +86,9 @@ litchi_sf = function(roi, if (!is.logical(grid)) { stop("grid has to be TRUE or FALSE") } + if (!is.logical(distancemethod)) { + stop("distancemethod has to be TRUE or FALSE") + } # Parameters calculated flight.speed.kmh = flight.params@flight.speed.kmh @@ -319,12 +318,13 @@ litchi_sf = function(roi, dfLitchi$heading.deg. = c(finalHeading, 90) dfLitchi$curvesize.m. = 0 dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 -# dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs * photos - dfLitchi$photo_timeinterval = flight.params@photo.interval * photos + if (distancemethod = TRUE) { + dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs * photos + } else { + dfLitchi$photo_timeinterval = flight.params@photo.interval * photos + } dfLitchi$gimbalmode = 2 # Interpolate gimbal position between waypoints dfLitchi$gimbalpitchangle = gimbal.pitch.angle -# dfLitchi$actiontype1 = 5 -# dfLitchi$actionparam1 = gimbal.pitch.angle # Split the flight if is too long dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) From d6297ceeeefb0ce9603f4723184fde933b7a0bc5 Mon Sep 17 00:00:00 2001 From: merkato Date: Sat, 26 Nov 2022 15:17:58 +0100 Subject: [PATCH 27/35] once again logic fix. --- R/litchi_sf.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/litchi_sf.R b/R/litchi_sf.R index 31e65d0..c2aedf9 100644 --- a/R/litchi_sf.R +++ b/R/litchi_sf.R @@ -318,10 +318,10 @@ litchi_sf = function(roi, dfLitchi$heading.deg. = c(finalHeading, 90) dfLitchi$curvesize.m. = 0 dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 - if (distancemethod = TRUE) { - dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs * photos - } else { + if (distancemethod != TRUE) { dfLitchi$photo_timeinterval = flight.params@photo.interval * photos + } else { + dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs * photos } dfLitchi$gimbalmode = 2 # Interpolate gimbal position between waypoints dfLitchi$gimbalpitchangle = gimbal.pitch.angle From f6dc9e9b0ca6e550a9e074a21589d55a161a5c4c Mon Sep 17 00:00:00 2001 From: merkato Date: Sun, 27 Nov 2022 05:35:35 +0100 Subject: [PATCH 28/35] =?UTF-8?q?ucz=C4=99=20si=C4=99.=20Learning=20is=20n?= =?UTF-8?q?ot=20easy,=20but=20fun?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- R/litchi_sf.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/litchi_sf.R b/R/litchi_sf.R index c2aedf9..61a4ec0 100644 --- a/R/litchi_sf.R +++ b/R/litchi_sf.R @@ -318,7 +318,7 @@ litchi_sf = function(roi, dfLitchi$heading.deg. = c(finalHeading, 90) dfLitchi$curvesize.m. = 0 dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 - if (distancemethod != TRUE) { + if (!isTRUE(distancemethod)) { dfLitchi$photo_timeinterval = flight.params@photo.interval * photos } else { dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs * photos From 2aaa7d0e2f5359db34dbc31cd83f5b6ebe8d1b29 Mon Sep 17 00:00:00 2001 From: merkato Date: Sun, 27 Nov 2022 10:13:50 +0100 Subject: [PATCH 29/35] flight plan summary function - WIP --- R/litchi_sf.R | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/R/litchi_sf.R b/R/litchi_sf.R index 61a4ec0..41c8208 100644 --- a/R/litchi_sf.R +++ b/R/litchi_sf.R @@ -504,3 +504,52 @@ litchi_sf = function(roi, return (waypoints) } + +#' Function to print flight plan summary +#' +#' This function will print messages with flight plan parameters, +#' like flight time, photo interval +#' +#' @rdname flight.summary +#' +#' @param flight.plan +#' +#' @examples +#' params = flight.parameters( +#' gsd = 4, +#' side.overlap = 0.8, +#' front.overlap = 0.8, +#' flight.speed.kmh = 54, +#' max.gsd = 0 +#' ) +#' +#' @export +flight.summary = function( + ) { + message("#####################") + message("## Flight settings ## ") + message("#####################") + message("Min shutter speed: ", appendLF = FALSE) + message(flight.params@minimum.shutter.speed) + message("Photo interval: ", appendLF = FALSE) + message(flight.params@photo.interval, appendLF = FALSE) + message(" s") + message("Photo distance: ", appendLF = FALSE) + message(flight.params@photo.interval * flight.params@flight.speed.kmh / 3.6, appendLF = FALSE) + message(" m") + message("Flight speed: ", appendLF = FALSE) + message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) + message(" km/h") + message("Total number of waypoints", appendLF = FALSE) + message(nrow(waypoints)) + message("Flight lines angle: ", appendLF = FALSE) + message(round(alpha, 4)) + message('Total flight time: ', appendLF = FALSE) + message(round(totalFlightTime, 4)) + return (params) +} + +# # Example +# # +# (PlanSummary = flight.summary( +# )) From dd523a2cb8e186b16e0f61d26e9a119bdcfe62a2 Mon Sep 17 00:00:00 2001 From: merkato Date: Sun, 27 Nov 2022 10:37:44 +0100 Subject: [PATCH 30/35] plan summary - WIP --- R/litchi_sf.R | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/R/litchi_sf.R b/R/litchi_sf.R index 41c8208..2f5bd58 100644 --- a/R/litchi_sf.R +++ b/R/litchi_sf.R @@ -512,15 +512,13 @@ litchi_sf = function(roi, #' #' @rdname flight.summary #' -#' @param flight.plan +#' @param flight.plan Flight Plan results. +#' @param flight.params Flight Parameters. parameters calculated from flight.parameters() #' #' @examples -#' params = flight.parameters( -#' gsd = 4, -#' side.overlap = 0.8, -#' front.overlap = 0.8, -#' flight.speed.kmh = 54, -#' max.gsd = 0 +#' plansummary = flight.summary( +#' flight.params, +#' flight.plan, #' ) #' #' @export @@ -541,15 +539,17 @@ flight.summary = function( message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) message(" km/h") message("Total number of waypoints", appendLF = FALSE) - message(nrow(waypoints)) + message(nrow(flight.plan@waypoints)) message("Flight lines angle: ", appendLF = FALSE) - message(round(alpha, 4)) + message(round(flight.plan@alpha, 4)) message('Total flight time: ', appendLF = FALSE) - message(round(totalFlightTime, 4)) - return (params) + message(round(totalflight.plan@FlightTime, 4)) + return () } # # Example # # # (PlanSummary = flight.summary( +# flight.params, +# flight.plan # )) From 7ee3c1048b0cc637dd7e98cbe860804bb7044d82 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Sun, 27 Nov 2022 13:59:54 +0100 Subject: [PATCH 31/35] flight.params --- NAMESPACE | 1 + R/litchi_sf.R | 79 ++---------------------- R/utils.R | 59 ++++++++++++++++-- man/litchi_sf.Rd | 12 ++-- mytest/lasek.gpkg | Bin 106496 -> 106496 bytes mytest/test_sf.R | 149 +++++++++++++++++++++++++++------------------- 6 files changed, 155 insertions(+), 145 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index f05ea2d..bf19586 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand export(flight.parameters) +export(flight.summary) export(litchi.plan) export(litchi_sf) import(rgdal) diff --git a/R/litchi_sf.R b/R/litchi_sf.R index 2f5bd58..d295d3b 100644 --- a/R/litchi_sf.R +++ b/R/litchi_sf.R @@ -346,8 +346,6 @@ litchi_sf = function(roi, splits = split.data.frame(dfLitchi, f = dfLitchi$split) -# --------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------- if (hasCustomLaunch) { p0x = launch[[1]][1] p0y = launch[[2]][1] @@ -447,8 +445,6 @@ litchi_sf = function(roi, splits[[newIdx]]$split = newIdx } } -# --------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------- if (nrow(waypoints) > MAX_WAYPOINTS) { message("Your flight was split into ", length(splits), " sub-flights, because the number of waypoints ", nrow(waypoints), " exceeds the maximum of ", MAX_WAYPOINTS, ".") @@ -480,76 +476,13 @@ litchi_sf = function(roi, } graphics::text(waypoints[,1], waypoints[,2], seq_along(waypoints[,1]), pos=3) + #update flight.params + flight.params@number.of.waypoints <- nWaypoints + flight.params@alpha <- alpha + flight.params@total.flight.time <- totalFlightTime - message("#####################") - message("## Flight settings ## ") - message("#####################") - message("Min shutter speed: ", appendLF = FALSE) - message(flight.params@minimum.shutter.speed) - message("Photo interval: ", appendLF = FALSE) - message(flight.params@photo.interval, appendLF = FALSE) - message(" s") - message("Photo distance: ", appendLF = FALSE) - message(flight.params@photo.interval * flight.params@flight.speed.kmh / 3.6, appendLF = FALSE) - message(" m") - message("Flight speed: ", appendLF = FALSE) - message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) - message(" km/h") - message("Total number of waypoints", appendLF = FALSE) - message(nrow(waypoints)) - message("Flight lines angle: ", appendLF = FALSE) - message(round(alpha, 4)) - message('Total flight time: ', appendLF = FALSE) - message(round(totalFlightTime, 4)) + # messages + flight.summary(flight.params) return (waypoints) } - -#' Function to print flight plan summary -#' -#' This function will print messages with flight plan parameters, -#' like flight time, photo interval -#' -#' @rdname flight.summary -#' -#' @param flight.plan Flight Plan results. -#' @param flight.params Flight Parameters. parameters calculated from flight.parameters() -#' -#' @examples -#' plansummary = flight.summary( -#' flight.params, -#' flight.plan, -#' ) -#' -#' @export -flight.summary = function( - ) { - message("#####################") - message("## Flight settings ## ") - message("#####################") - message("Min shutter speed: ", appendLF = FALSE) - message(flight.params@minimum.shutter.speed) - message("Photo interval: ", appendLF = FALSE) - message(flight.params@photo.interval, appendLF = FALSE) - message(" s") - message("Photo distance: ", appendLF = FALSE) - message(flight.params@photo.interval * flight.params@flight.speed.kmh / 3.6, appendLF = FALSE) - message(" m") - message("Flight speed: ", appendLF = FALSE) - message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) - message(" km/h") - message("Total number of waypoints", appendLF = FALSE) - message(nrow(flight.plan@waypoints)) - message("Flight lines angle: ", appendLF = FALSE) - message(round(flight.plan@alpha, 4)) - message('Total flight time: ', appendLF = FALSE) - message(round(totalflight.plan@FlightTime, 4)) - return () -} - -# # Example -# # -# (PlanSummary = flight.summary( -# flight.params, -# flight.plan -# )) diff --git a/R/utils.R b/R/utils.R index 4ac1f9f..ed9eb95 100644 --- a/R/utils.R +++ b/R/utils.R @@ -217,7 +217,6 @@ getAngles = function(waypoints) { angles } - # --------------------------------------------------------------------------------------------- # from https://github.com/caiohamamura/flightplanning-R/pull/4/commits/1b43add396c8ed9020c1f06863d4e7c8aef7355d # Calculate distance in meters between two points @@ -242,7 +241,54 @@ earth.dist <- function (long1, lat1, long2, lat2) } # --------------------------------------------------------------------------------------------- +#' Function to print flight plan summary +#' +#' This function will print messages with flight plan parameters, +#' like flight time, photo interval +#' +#' @rdname flight.summary +#' +#' @param flight.params Flight Parameters. parameters calculated from flight.parameters() +#' +#' @examples +#' +#' flight.params = flightplanning::flight.parameters( +#' gsd = 4, +#' side.overlap = 0.8, +#' front.overlap = 0.8, +#' flight.speed.kmh = 54 +#' ) +#' plansummary = flight.summary(flight.params) +#' +#' @export +flight.summary = function(flight.params) { + if(inherits(flight.params, "Flight Parameters")) { + message("#####################") + message("## Flight settings ## ") + message("#####################") + message("Min shutter speed: ", appendLF = FALSE) + message(flight.params@minimum.shutter.speed) + message("Photo interval: ", appendLF = FALSE) + message(flight.params@photo.interval, appendLF = FALSE) + message(" s") + message("Photo distance: ", appendLF = FALSE) + message(flight.params@photo.interval * flight.params@flight.speed.kmh / 3.6, appendLF = FALSE) + message(" m") + message("Flight speed: ", appendLF = FALSE) + message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) + message(" km/h") + message("Total number of waypoints: ", appendLF = FALSE) + message(flight.params@number.of.waypoints) + message("Flight lines angle: ", appendLF = FALSE) + message(round(flight.params@alpha, 4)) + message('Total flight time: ', appendLF = FALSE) + message(round(flight.params@total.flight.time, 4)) + } else { + message(paste("Seems", flight.params, "are not of class \"Flight Parameters\"")) + } +} +# --------------------------------------------------------------------------------------------- #' Class for Flight Parameters setClass("Flight Parameters", slots = c( @@ -253,7 +299,10 @@ setClass("Flight Parameters", height = "numeric", ground.height = "numeric", minimum.shutter.speed = "character", - photo.interval = "numeric" + photo.interval = "numeric", + number.of.waypoints = "numeric", + alpha = "numeric", + total.flight.time = "numeric" ), prototype = list( flight.line.distance = NA_real_, @@ -263,7 +312,9 @@ setClass("Flight Parameters", ground.height = NA_real_, height = NA_real_, minimum.shutter.speed = NA_character_, - photo.interval = NA_real_ + photo.interval = NA_real_, + number.of.waypoints = NA_real_, + alpha = NA_real_, + total.flight.time = NA_real_ ) ) - diff --git a/man/litchi_sf.Rd b/man/litchi_sf.Rd index 3450666..09c67e7 100644 --- a/man/litchi_sf.Rd +++ b/man/litchi_sf.Rd @@ -14,7 +14,8 @@ litchi_sf( max.flight.time = 15, starting.point = 1, launch = list(0, 0), - grid = FALSE + grid = FALSE, + distancemethod = FALSE ) } \arguments{ @@ -40,6 +41,8 @@ time, it will be splitted into smaller missions.} \item{launch}{list(0,0) launch point coordinates (x, y), has to be provided in the same metric CRS as roi} \item{grid}{logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular} + +\item{distancemethod}{logical (default FALSE). Change shutter interval from time to distance} } \value{ A data frame with the waypoints calculated for the flight plan @@ -47,13 +50,6 @@ A data frame with the waypoints calculated for the flight plan \description{ Function to generate Litchi csv flight plan } -\note{ -this function will feed the csv flight plan with the `gimbal.pitch.angle` -and the `photo time interval` for each waypoint, but those are not supported -by Litchi yet, although they are present in the exported csv from the -Litchi hub platform, though it may be supported in the future; when it does -the function will already work with this feature. -} \examples{ library(flightplanning) diff --git a/mytest/lasek.gpkg b/mytest/lasek.gpkg index 7dc8ac733c70a9ddd9e88063890dd0c8b4eddc9b..91e89362f00dbb699a4b18df3f3ec1f7fd34350d 100644 GIT binary patch delta 22 ecmZoTz}9epZGtqT^h6nFM(M_ctqF_^<^up$=LePm delta 22 ecmZoTz}9epZGtqT 1) { roi <- sf::st_union(roi) |> @@ -23,23 +21,28 @@ if(nrow(roi) > 1) { output <- "mytest/fly.csv" -params <- flight.parameters( +params <- flightplanning::flight.parameters( height = 120, focal.length35 = 24, flight.speed.kmh = 24, - side.overlap = 0.91, + side.overlap = 0.8, front.overlap = 0.7 ) -litchi_sf(roi, - output, - params, - gimbal.pitch.angle = -90, - flight.lines.angle = -1, - max.waypoints.distance = 400, - max.flight.time = 18, - grid = FALSE +flightplanning::litchi_sf(roi, + output, + params, + gimbal.pitch.angle = -90, + flight.lines.angle = -1, + max.waypoints.distance = 2000, + max.flight.time = 15, + starting.point = 1, + grid = FALSE + # , + # launch = list(344800, 383000) ) +flightplanning::flight.summary(params) + flight.params <- params gimbal.pitch.angle = -90 @@ -49,22 +52,11 @@ max.flight.time = 18 starting.point = 1 launch = list(344800, 383000) grid = FALSE +distancemethod = FALSE -flightplanning::litchi_sf(roi, - output, - params, - gimbal.pitch.angle = -90, - flight.lines.angle = -1, - max.waypoints.distance = 2000, - max.flight.time = 15, - starting.point = 1, - grid = FALSE, - launch = list(344800, 383000) -) +MAX_WAYPOINTS = 99 -# --------------------------------------------------------------------------------------------- -# --------------------------------------------------------------------------------------------- #' Function to generate Litchi csv flight plan #' #' @rdname litchi_sf @@ -84,12 +76,7 @@ flightplanning::litchi_sf(roi, #' @param starting.point numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 #' @param launch list(0,0) launch point coordinates (x, y), has to be provided in the same metric CRS as roi #' @param grid logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular -#' -#' @note this function will feed the csv flight plan with the `gimbal.pitch.angle` -#' and the `photo time interval` for each waypoint, but those are not supported -#' by Litchi yet, although they are present in the exported csv from the -#' Litchi hub platform, though it may be supported in the future; when it does -#' the function will already work with this feature. +#' @param distancemethod logical (default FALSE). Change shutter interval from time to distance #' #' @examples #' library(flightplanning) @@ -132,7 +119,8 @@ litchi_sf = function(roi, max.flight.time = 15, starting.point = 1, launch = list(0, 0), - grid = FALSE) { + grid = FALSE, + distancemethod = FALSE) { # Check parameters if (class(roi)[1] != "sf") { @@ -154,6 +142,9 @@ litchi_sf = function(roi, if (!is.logical(grid)) { stop("grid has to be TRUE or FALSE") } + if (!is.logical(distancemethod)) { + stop("distancemethod has to be TRUE or FALSE") + } # Parameters calculated flight.speed.kmh = flight.params@flight.speed.kmh @@ -281,11 +272,14 @@ litchi_sf = function(roi, angle = alpha, minAngle = 80) row.names(adjustedCurves) <- adjustedCurves$index + # Concatenate regular waypoints with curve waypoints wptsMatrix = as.data.frame(matrix(nrow=nrow(waypoints)+nrow(adjustedCurves),ncol=4)) colnames(wptsMatrix) = colnames=c("x", "y", "isCurve", "takePhoto") mat_pos = 1 for (i in seq_len(nrow(waypoints))) { + # i <- 6 + # mat_pos <- 11 curve = as.vector(adjustedCurves[as.character(i), ]) hasCurve = !anyNA(curve) if (hasCurve) { @@ -375,17 +369,18 @@ litchi_sf = function(roi, dfLitchi$latitude = lats dfLitchi$longitude = lngs dfLitchi$altitude.m. = flight.params@height - dfLitchi$altitudemode = 1 + dfLitchi$altitudemode = 1 # AGL handling dfLitchi$speed.m.s. = flightSpeedMs dfLitchi$heading.deg. = c(finalHeading, 90) dfLitchi$curvesize.m. = 0 dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 - dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs * photos - dfLitchi$photo_timeinterval = flight.params@photo.interval * photos + if (!isTRUE(distancemethod)) { + dfLitchi$photo_timeinterval = flight.params@photo.interval * photos + } else { + dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs * photos + } + dfLitchi$gimbalmode = 2 # Interpolate gimbal position between waypoints dfLitchi$gimbalpitchangle = gimbal.pitch.angle - dfLitchi$actiontype1 = 5 - dfLitchi$actionparam1 = gimbal.pitch.angle - # Split the flight if is too long dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) @@ -407,15 +402,14 @@ litchi_sf = function(roi, splits = split.data.frame(dfLitchi, f = dfLitchi$split) -# --------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------- if (hasCustomLaunch) { p0x = launch[[1]][1] p0y = launch[[2]][1] - wgs84 = "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0" message("adding custom launch point to submissions") - # launch84 = rgdal::rawTransform(roi@proj4string@projargs, wgs84, as.integer(1), launch[[1]], launch[[2]]) launch84 <- sf::st_point(x = c(launch[[1]], launch[[2]])) |> sf::st_sfc(crs = roiCRS) |> sf::st_transform(crs = "EPSG:4326") |> @@ -430,22 +424,18 @@ litchi_sf = function(roi, splits[[i]] = rbind(overage, splits[[i]]) } - # i <- 1 - # mercator = rgdal::rawTransform(wgs84, roi@proj4string@projargs, nrow(splits[[i]]), splits[[i]]$longitude, splits[[i]]$latitude) mercator = splits[[i]] |> sf::st_as_sf(coords = c("longitude", "latitude"), crs = "EPSG:4326") |> sf::st_transform(crs = roiCRS) |> subset(select = "geometry") |> sf::st_coordinates() - # p1x = mercator[[1]][1] - # p1y = mercator[[2]][1] p1x = as.numeric(mercator[1, 1]) p1y = as.numeric(mercator[1, 2]) dx = p1x - p0x dy = p1y - p0y - distance = flightplanning:::earth.dist(launch84[[1]][1], launch84[[2]][1], splits[[i]]$longitude[1], splits[[i]]$latitude[1]) + distance = earth.dist(launch84[[1]][1], launch84[[2]][1], splits[[i]]$longitude[1], splits[[i]]$latitude[1]) interpPtsToAdd = floor(distance / max.waypoints.distance) nPtsToAdd = 1 + interpPtsToAdd @@ -470,23 +460,15 @@ litchi_sf = function(roi, } } - # wgs84D = rgdal::rawTransform(roi@proj4string@projargs, wgs84, nrow(toConvert), toConvert$lat, toConvert$lon) - wgs84D = toConvert |> sf::st_as_sf(coords = c("lon", "lat"), crs = roiCRS) |> sf::st_transform(crs = "EPSG:4326") |> subset(select = "geometry") |> sf::st_coordinates() - - # ptsToAdd$latitude = wgs84D[[2]] - # ptsToAdd$longitude = wgs84D[[1]] - ptsToAdd$latitude = wgs84D[ ,2] ptsToAdd$longitude = wgs84D[ ,1] - - splitSize = nrow(splits[[i]]) totalSize = splitSize + nPtsToAdd rem = 0 @@ -521,9 +503,8 @@ litchi_sf = function(roi, splits[[newIdx]]$split = newIdx } } - -# --------------------------------------------------------------------------------------------- - + # --------------------------------------------------------------------------------------------- + # --------------------------------------------------------------------------------------------- if (nrow(waypoints) > MAX_WAYPOINTS) { message("Your flight was split into ", length(splits), " sub-flights, because the number of waypoints ", nrow(waypoints), " exceeds the maximum of ", MAX_WAYPOINTS, ".") @@ -540,7 +521,7 @@ litchi_sf = function(roi, write.csv(dataSplit[,-ncol(dataSplit)], output2, row.names = FALSE) message(output2) } - output2 = paste0(first, "_entire", second) + output2 = paste0(first, "entire", second) write.csv(dfLitchi, output2, row.names = FALSE) message("The entire flight plan was saved as:") message(output2) @@ -578,9 +559,57 @@ litchi_sf = function(roi, message(round(totalFlightTime, 4)) return (waypoints) +} +#' Function to print flight plan summary +#' +#' This function will print messages with flight plan parameters, +#' like flight time, photo interval +#' +#' @rdname flight.summary +#' +#' @param flight.plan Flight Plan results. +#' @param flight.params Flight Parameters. parameters calculated from flight.parameters() +#' +#' @examples +#' plansummary = flight.summary( +#' flight.params, +#' flight.plan, +#' ) +#' +#' @export +flight.summary = function( +) { + message("#####################") + message("## Flight settings ## ") + message("#####################") + message("Min shutter speed: ", appendLF = FALSE) + message(flight.params@minimum.shutter.speed) + message("Photo interval: ", appendLF = FALSE) + message(flight.params@photo.interval, appendLF = FALSE) + message(" s") + message("Photo distance: ", appendLF = FALSE) + message(flight.params@photo.interval * flight.params@flight.speed.kmh / 3.6, appendLF = FALSE) + message(" m") + message("Flight speed: ", appendLF = FALSE) + message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) + message(" km/h") + message("Total number of waypoints", appendLF = FALSE) + message(nrow(flight.plan@waypoints)) + message("Flight lines angle: ", appendLF = FALSE) + message(round(flight.plan@alpha, 4)) + message('Total flight time: ', appendLF = FALSE) + message(round(totalflight.plan@FlightTime, 4)) + return () } +# # Example +# # +# (PlanSummary = flight.summary( +# flight.params, +# flight.plan +# )) + f <- list.files(path = "mytest", pattern = ".csv", full.names = TRUE) unlink(f) From f2d9701c3a037fc9b121eb60e6f808ba25eeaea2 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Sun, 27 Nov 2022 14:04:56 +0100 Subject: [PATCH 32/35] flight.summary doc --- man/flight.summary.Rd | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 man/flight.summary.Rd diff --git a/man/flight.summary.Rd b/man/flight.summary.Rd new file mode 100644 index 0000000..00cfa80 --- /dev/null +++ b/man/flight.summary.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{flight.summary} +\alias{flight.summary} +\title{Function to print flight plan summary} +\usage{ +flight.summary(flight.params) +} +\arguments{ +\item{flight.params}{Flight Parameters. parameters calculated from flight.parameters()} +} +\description{ +This function will print messages with flight plan parameters, +like flight time, photo interval +} +\examples{ + +flight.params = flightplanning::flight.parameters( + gsd = 4, + side.overlap = 0.8, + front.overlap = 0.8, + flight.speed.kmh = 54 +) +plansummary = flight.summary(flight.params) + +} From 61bae3b7df23ac319fd0b0beabae4d4bace27be5 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Sun, 27 Nov 2022 23:27:11 +0100 Subject: [PATCH 33/35] logical and --- R/main.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/main.R b/R/main.R index ad0de22..d2a6772 100644 --- a/R/main.R +++ b/R/main.R @@ -407,11 +407,10 @@ flight.parameters = function( flight.speed.kmh = 54, max.gsd = 0) { - if (is.na(gsd) == is.na(height)) { + if (is.na(gsd) & is.na(height)) { stop("You must specify either gsd or height!") } - image.diag.px = sqrt(image.width.px^2 + image.height.px^2) if (is.na(gsd)) { mult.factor = (height / focal.length35) From 9a4e764ef2f7f8cee853b5393bf153ea9603ce06 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Tue, 15 Oct 2024 21:44:29 +0200 Subject: [PATCH 34/35] Cleanup: removed lichi.plan function and it's dependencies: rgeos, rgdal, sp --- DESCRIPTION | 3 - NAMESPACE | 6 - R/main.R | 367 +----------------------------------------- man/litchi.plan.Rd | 81 ---------- tests/testthat/test.R | 14 +- 5 files changed, 8 insertions(+), 463 deletions(-) delete mode 100644 man/litchi.plan.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 3e8bc4b..a2b2677 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -15,9 +15,6 @@ Imports: graphics, grDevices, methods, - rgdal, - rgeos, - sp, shotGroups, sf Depends: R (>= 3.0) diff --git a/NAMESPACE b/NAMESPACE index bf19586..e417938 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,18 +2,12 @@ export(flight.parameters) export(flight.summary) -export(litchi.plan) export(litchi_sf) -import(rgdal) -import(rgeos) import(sf) importFrom(grDevices,chull) importFrom(graphics,text) importFrom(methods,slot) importFrom(shotGroups,getMinBBox) -importFrom(sp,Line) -importFrom(sp,Lines) -importFrom(sp,SpatialLines) importFrom(utils,data) importFrom(utils,read.csv) importFrom(utils,write.csv) diff --git a/R/main.R b/R/main.R index d2a6772..d174bdf 100644 --- a/R/main.R +++ b/R/main.R @@ -2,371 +2,6 @@ MIN_PHOTO_INTERVAL = 2 DIAG_35MM = sqrt(36^2 + 24^2) # Classical 35mm film diagonal MAX_WAYPOINTS = 99 -#' Function to generate Litchi csv flight plan -#' -#' -#' @rdname litchi.plan -#' -#' @return A data frame with the waypoints calculated for the flight plan -#' -#' @param roi range of interest loaded as an OGR layer, must be in -#' a metric units projection for working properly -#' @param output output path for the csv file -#' @param flight.params Flight Parameters. parameters calculated from flight.parameters() -#' @param gimbal.pitch.angle gimbal angle for taking photos, default -90 (overriden at flight time) -#' @param flight.lines.angle angle for the flight lines, default -1 (auto set based on larger direction) -#' @param max.waypoints.distance maximum distance between waypoints in meters, -#' default 2000 (some issues have been reported with distances > 2 Km) -#' @param max.flight.time maximum flight time. If mission is greater than the estimated -#' time, it will be splitted into smaller missions. -#' @param starting.point numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1 -#' @param grid logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular -#' -#' @note this function will feed the csv flight plan with the `gimbal.pitch.angle` -#' and the `photo time interval` for each waypoint, but those are not supported -#' by Litchi yet, although they are present in the exported csv from the -#' Litchi hub platform, though it may be supported in the future; when it does -#' the function will already work with this feature. -#' -#' @examples -#' library(flightplanning) -#' library(rgdal) -#' -#' exampleBoundary = readOGR( -#' system.file("extdata", -#' "exampleBoundary.shp", -#' package="flightplanning" -#' ), -#' "exampleBoundary") -#' outPath = tempfile(fileext=".csv") -#' -#' flight.params = flight.parameters( -#' gsd = 4, -#' side.overlap = 0.8, -#' front.overlap = 0.8, -#' flight.speed.kmh = 54 -#' ) -#' -#' litchi.plan(exampleBoundary, -#' outPath, -#' flight.params, -#' flight.lines.angle = -1, -#' max.waypoints.distance = 2000, -#' max.flight.time = 15) -#' -#' -#' @export -#' @import rgeos rgdal -#' @importFrom graphics text -#' @importFrom shotGroups getMinBBox -#' @importFrom methods slot -#' @importFrom sp Line Lines SpatialLines -#' @importFrom utils data read.csv write.csv -#' -litchi.plan = function(roi, - output, - flight.params, - gimbal.pitch.angle = -90, - flight.lines.angle = -1, - max.waypoints.distance = 2000, - max.flight.time = 15, - starting.point = 1, - grid = FALSE) { - # Check parameters - if (class(roi)[1] == "sf") { - roi <- sf::as_Spatial(roi) - } - - if (class(roi)[1] != "SpatialPolygonsDataFrame") - stop("ROI is not a valid polygon layer") - if (length(grep("units=m", as.character(roi@proj4string@projargs))) == 0) - stop("ROI is not in a metric projection") - if (methods::is(flight.params)[1] != "Flight Parameters") - stop("Flight parameters is not an instance returned from flight.parameters()") - # TODO add a test - if (!is.logical(grid)) { - stop("grid has to be TRUE or FALSE") - } - - # Parameters calculated - flight.speed.kmh = flight.params@flight.speed.kmh - flightSpeedMs = flight.speed.kmh / 3.6 - height = flight.params@height - groundHeight = flight.params@ground.height - groundHeightOverlap = groundHeight * flight.params@front.overlap - flightLineDistance = flight.params@flight.line.distance - vertices = roi@polygons[[1]]@Polygons[[1]]@coords - - # Get bounding box parameters - if (flight.lines.angle != -1) { - minBbox = getBBoxAngle(vertices, flight.lines.angle) - } else { - # if angle not specified use minimum possible bounding box - minBbox = shotGroups::getMinBBox(vertices) -# minBbox = getMinBBox(vertices) - } - - if (grid == FALSE) { - width = minBbox$height - height = minBbox$width - alpha = minBbox$angle - } else { - width = minBbox$width - height = minBbox$height - alpha = minBbox$angle-90 - } - - rads = alpha*pi/180 - centroid = apply(minBbox$pts, 2, mean) - - # Calculate points offset from centroid - # based on angle and width/height offsets - # width offsets (between flightlines) - nLines = ceiling(width / flightLineDistance) + 1 - xWidths = (-nLines/2):(nLines/2) * flightLineDistance - xWidths = rep(xWidths, each=2) - - # heights offset (one for upper half - # one for lower half) - heightDistance = groundHeight-groundHeightOverlap - heightAdjusted = height + 2*heightDistance - # Put offset to avoid intersection issues - heightAdjusted = heightAdjusted + heightDistance*2 - heightMHalf = -heightAdjusted/2 - heightPHalf = heightAdjusted/2 - yHeights = c(heightMHalf, heightPHalf) - - - # Switch position of the first point - if (starting.point == 2) { - yHeights = c(heightPHalf, heightMHalf) - } else if (starting.point == 3) { - xWidths = rev(xWidths) - yHeights = c(heightPHalf, heightMHalf) - } else if (starting.point == 4) { - xWidths = rev(xWidths) - } - - # Interleave one upper, two bottom - # two upper, two bottom... until end - yHeights = c(rep(c(yHeights, rev(yHeights)), nLines/2+1)) - yHeights = yHeights[1:length(xWidths)] - - - # Calculate translated x and y from - # angles and offsets from centroid - xys = data.frame( - x = -xWidths * sin(rads) + - yHeights * cos(rads), - y = xWidths * cos(rads) + - yHeights * sin(rads)) - - # Initial waypoints to intersect waypoints - waypoints = xys + rep(centroid, each=nrow(xys)) - - ################################################# - # Intersect each flight line from bounding box - # to match actual ROI - ################################################# - # For some reason gIntersection with MULTILINESTRING - # will return linestrings in inconsistent order - # though it will be done in a for loop - - # - # -# wktLines = paste(apply(waypoints, 1, paste, collapse=" "), collapse=", ") -# wktLines = paste("LINESTRING(", wktLines,")") -# gLines = rgeos::readWKT(wktLines, p4s = roi@proj4string) -# inter = rgeos::gIntersection(rgeos::gBuffer(roi, width = flightLineDistance), gLines) -# nLines = length(inter@lines[[1]]@Lines) - -# flightLines = t(sapply(inter@lines[[1]]@Lines, function(x) x@coords)) - -# RSB - glist <- vector(mode="list", length=nrow(waypoints)-1) - for (i in seq_along(glist)) glist[[i]] <- sp::Lines(list(sp::Line(waypoints[c(i, (i+1)),])), ID=as.character(i)) - gLines <- sp::SpatialLines(glist, proj4string=slot(roi, "proj4string")) - inter = rgeos::gIntersection(rgeos::gBuffer(roi, width = flightLineDistance), gLines, byid=TRUE) - nLines <- length(inter) - flightLines <- t(sapply(slot(inter, "lines"), function(x) slot(slot(x, "Lines")[[1]], "coords"))) - -# RSB - flightLines = flightLines[,c(1,3,2,4)] - - - waypoints = matrix(nrow=nLines * 2, ncol=2) - waypoints[seq(1, nLines*2, 2),] = flightLines[, 1:2] - waypoints[seq(2, nLines*2, 2),] = flightLines[, 3:4] - - # Calculate curves points to allow smooth curves - curvedPoints = outerCurvePoints(waypoints = waypoints, - angle = alpha, - flightLineDistance = flightLineDistance) - - # Adjust curve points position to avoid acute angles - adjustedCurves = adjustAcuteAngles(xy = curvedPoints, - angle = alpha, - minAngle = 80) - - # Concatenate regular waypoints with curve waypoints - wgs84 = "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0" - wptsMatrix = as.data.frame(matrix(nrow=nrow(waypoints)+nrow(adjustedCurves),ncol=4)) - colnames(wptsMatrix) = colnames=c("x", "y", "isCurve", "takePhoto") - mat_pos = 1 - for (i in seq_len(nrow(waypoints))) { - curve = as.vector(adjustedCurves[as.character(i),]) - - # From R 4.2.0 onwards: - # "as.vector() gains a data.frame method which returns a simple - # named list, also clearing a long standing ‘FIXME’ to enable - # as.vector(, mode="list"). This breaks code relying - # on as.vector() to return the unchanged data frame." - # therefore changing curve[,1:2] to curve[1,2] and removing cbind - - hasCurve = !anyNA(curve) - if (hasCurve) { - if (curve$before) { - wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) - mat_pos = mat_pos + 1 - wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) - mat_pos = mat_pos + 1 - } else { - wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) - mat_pos = mat_pos + 1 - wptsMatrix[mat_pos,] = c(curve[1:2], TRUE, FALSE) - mat_pos = mat_pos + 1 - } - } else { - wptsMatrix[mat_pos,] = c(waypoints[i, 1:2], FALSE, i %% 2 == 1) - mat_pos = mat_pos + 1 - } - } - waypoints = wptsMatrix - - # Break if distance greater than the maxWaypointDistance - waypointsXY = waypoints[, c("x", "y")] - distances = sqrt(diff(waypoints$x)**2 + diff(waypoints$y)**2) - breakIdx = distances > max.waypoints.distance - - newSize = nrow(waypoints) + sum(breakIdx) - if (newSize != nrow(waypoints)) { - midpoints = (waypointsXY[breakIdx,] + waypointsXY[-1,][breakIdx,])/2 - waypoints2 = data.frame(x = numeric(newSize), - y = numeric(newSize), - isCurve = FALSE, - takePhoto = TRUE) - - pos = seq_along(breakIdx)[breakIdx] - idx = pos + order(pos) - waypoints2[idx,1:2] = midpoints - waypoints2[-idx,] = waypoints - waypoints = waypoints2 - } - - - # Transform to WGS84 latitude and longitude - transform = rgdal::rawTransform(roi@proj4string@projargs, wgs84, n=nrow(waypoints), x=waypoints[,1], y=waypoints[,2]) - lats = transform[[2]] - lngs = transform[[1]] - photos = waypoints[,4] - graphics::plot(waypoints[,1:2]) - graphics::polygon(roi@polygons[[1]]@Polygons[[1]]@coords) - - - # Calculate heading - nWaypoints = nrow(waypoints) - latDiff = lats[-1]-lats[-nWaypoints] - lngDiff = lngs[-1]-lngs[-nWaypoints] - headDegree = atan(latDiff/lngDiff)/pi*180 - finalHeading = 270-headDegree - finalHeading[lngDiff > 0] = 90-headDegree[lngDiff > 0] - - # Set parameters of the flight in the CSV - dfLitchi = read.csv(system.file("extdata/litchi.csv", package = "flightplanning")) - dfLitchi = dfLitchi[rep(1, length(lats)),] - dfLitchi$latitude = lats - dfLitchi$longitude = lngs - dfLitchi$altitude.m. = flight.params@height - dfLitchi$altitudemode = 1 # Above ground (Fixed AGL) - dfLitchi$speed.m.s. = flightSpeedMs - dfLitchi$heading.deg. = c(finalHeading, 90) - dfLitchi$curvesize.m. = 0 - dfLitchi$curvesize.m.[waypoints$isCurve==1] = flightLineDistance*0.5 -# dfLitchi$photo_distinterval = flight.params@photo.interval * flightSpeedMs - dfLitchi$photo_timeinterval = flight.params@photo.interval * photos - dfLitchi$gimbalmode = 2 # Interpolated between waypoints - dfLitchi$gimbalpitchangle = gimbal.pitch.angle -# dfLitchi$actiontype1 = 5 -# dfLitchi$actionparam1 = gimbal.pitch.angle - - # Split the flight if is too long - dists = sqrt(diff(waypoints[,1])**2+diff(waypoints[,2])**2) - distAcum = c(0,cumsum(dists)) - flightTime = distAcum / (flightSpeedMs*0.75) / 60 - finalSize = nrow(dfLitchi) - totalFlightTime = flightTime[finalSize] - dfLitchi$split = 1 - if (totalFlightTime > max.flight.time) { - indexes = seq_len(finalSize) - nBreaks = ceiling(totalFlightTime/max.flight.time) - breaks = seq(0, flightTime[finalSize], length.out = nBreaks+1)[c(-1, -nBreaks-1)] - endWaypointsIndex = indexes[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] - endWaypoints = flightTime[waypoints$isCurve & (seq_len(finalSize) %% 2 == 0)] - selected = sapply(breaks, function(x) which.min(abs(endWaypoints-x))) - waypointsBreak = endWaypointsIndex[indexes[selected]] - - - dfLitchi$split = rep(1:nBreaks, diff(c(0, waypointsBreak, finalSize))) - splits = split.data.frame(dfLitchi, f = dfLitchi$split) - message("Your flight was splitted in ", length(splits), " splits, -because the total time would be ", round(totalFlightTime, 2), " minutes.") - message("They were saved as:") - first = substr(output, 1, nchar(output)-4) - second = substr(output, nchar(output)-3, nchar(output)) - for (dataSplit in splits) { - i = dataSplit[1, ]$split - output2 = paste0(first, i, second) - write.csv(dataSplit[,-ncol(dataSplit)], output2, row.names = FALSE) - message(output2) - } - output2 = paste0(first, "_entire", second) - write.csv(dfLitchi, output2, row.names = FALSE) - message("The entire flight plan was saved as:") - message(output2) - } else { - write.csv(dfLitchi, output, row.names = FALSE) - } - - colors = grDevices::rainbow(length(unique(dfLitchi$split))) - for (i in unique(dfLitchi$split)) - { - graphics::lines(waypoints[dfLitchi$split == i,1:2], lty=2, col=colors[as.integer(i)]) - } - graphics::text(waypoints[,1], waypoints[,2], seq_along(waypoints[,1]), pos=3) - - - message("#####################") - message("## Flight settings ## ") - message("#####################") - message("Min shutter speed: ", appendLF = FALSE) - message(flight.params@minimum.shutter.speed) - message("Photo interval: ", appendLF = FALSE) - message(flight.params@photo.interval, appendLF = FALSE) - message(" s") - message("Flight speed: ", appendLF = FALSE) - message(round(flight.params@flight.speed.kmh, 4), appendLF = FALSE) - message(" km/h") - message("Flight lines angle: ", appendLF = FALSE) - message(round(alpha, 4)) - message('Total flight time: ', appendLF = FALSE) - message(round(totalFlightTime, 4)) - message('Distance between flyin lines: ', appendLF = FALSE) - message(round(flightLineDistance, 3)) - message('', appendLF = TRUE) - - return (waypoints) -} - #' Function to calculate flight parameters @@ -407,7 +42,7 @@ flight.parameters = function( flight.speed.kmh = 54, max.gsd = 0) { - if (is.na(gsd) & is.na(height)) { + if ((is.na(gsd) & is.na(height)) || (!is.na(gsd) & !is.na(height))) { stop("You must specify either gsd or height!") } diff --git a/man/litchi.plan.Rd b/man/litchi.plan.Rd deleted file mode 100644 index 7b0de90..0000000 --- a/man/litchi.plan.Rd +++ /dev/null @@ -1,81 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/main.R -\name{litchi.plan} -\alias{litchi.plan} -\title{Function to generate Litchi csv flight plan} -\usage{ -litchi.plan( - roi, - output, - flight.params, - gimbal.pitch.angle = -90, - flight.lines.angle = -1, - max.waypoints.distance = 2000, - max.flight.time = 15, - starting.point = 1, - grid = FALSE -) -} -\arguments{ -\item{roi}{range of interest loaded as an OGR layer, must be in -a metric units projection for working properly} - -\item{output}{output path for the csv file} - -\item{flight.params}{Flight Parameters. parameters calculated from flight.parameters()} - -\item{gimbal.pitch.angle}{gimbal angle for taking photos, default -90 (overriden at flight time)} - -\item{flight.lines.angle}{angle for the flight lines, default -1 (auto set based on larger direction)} - -\item{max.waypoints.distance}{maximum distance between waypoints in meters, -default 2000 (some issues have been reported with distances > 2 Km)} - -\item{max.flight.time}{maximum flight time. If mission is greater than the estimated -time, it will be splitted into smaller missions.} - -\item{starting.point}{numeric (1, 2, 3 or 4). Change position from which to start the flight, default 1} - -\item{grid}{logical (default FALSE). Change direction of the fly lines over polygon from parallel to perpendicular} -} -\value{ -A data frame with the waypoints calculated for the flight plan -} -\description{ -Function to generate Litchi csv flight plan -} -\note{ -this function will feed the csv flight plan with the `gimbal.pitch.angle` -and the `photo time interval` for each waypoint, but those are not supported -by Litchi yet, although they are present in the exported csv from the -Litchi hub platform, though it may be supported in the future; when it does -the function will already work with this feature. -} -\examples{ -library(flightplanning) -library(rgdal) - -exampleBoundary = readOGR( - system.file("extdata", - "exampleBoundary.shp", - package="flightplanning" - ), - "exampleBoundary") -outPath = tempfile(fileext=".csv") - -flight.params = flight.parameters( - gsd = 4, - side.overlap = 0.8, - front.overlap = 0.8, - flight.speed.kmh = 54 -) - -litchi.plan(exampleBoundary, - outPath, - flight.params, - flight.lines.angle = -1, - max.waypoints.distance = 2000, - max.flight.time = 15) - - -} diff --git a/tests/testthat/test.R b/tests/testthat/test.R index 1fd943e..23cec68 100644 --- a/tests/testthat/test.R +++ b/tests/testthat/test.R @@ -1,5 +1,5 @@ params = flight.parameters(height = 100) -TOLERANCE = 1e-4 +# TOLERANCE = 1e-4 test_that("Flight parameters must have either gsd or height set up", { expect_error( flight.parameters(gsd=NA, height=NA) ) @@ -19,7 +19,7 @@ test_that("GSD calculation from height is correct", { image.width.px = 5472, image.height.px = 3648, flight.speed.kmh = 43.2) - expect_equal( params@gsd, 3.289473684210527, tolerance = TOLERANCE ) + expect_equal( params@gsd, 3.289473684210527, tolerance = 1e-4 ) }) @@ -29,7 +29,7 @@ test_that("Height calculation from GSD is correct", { image.width.px = 5472, image.height.px = 3648, flight.speed.kmh = 54) - expect_equal( params@height, 152, tolerance = TOLERANCE ) + expect_equal( params@height, 152, tolerance = 1e-4 ) }) test_that("Flight parameters front overlap is the same as input", { @@ -40,7 +40,7 @@ test_that("Flight parameters front overlap is the same as input", { image.height.px = 3648, flight.speed.kmh = 43, front.overlap = front.overlap) - expect_equal( params@front.overlap, front.overlap, tolerance = TOLERANCE ) + expect_equal( params@front.overlap, front.overlap, tolerance = 1e-4 ) }) @@ -57,7 +57,7 @@ test_that("Side overlap is correct", { flight.speed.kmh = 43.2, side.overlap = side.overlap) - expect_equal( params@flight.line.distance, overlap.meters, tolerance = TOLERANCE ) + expect_equal( params@flight.line.distance, overlap.meters, tolerance = 1e-4 ) }) @@ -78,7 +78,7 @@ test_that("Front overlap is correct", { flight.speed.kmh = speed.kmh, front.overlap = front.overlap) - expect_equal( params@photo.interval, interval, tolerance = TOLERANCE ) + expect_equal( params@photo.interval, interval, tolerance = 1e-4 ) }) @@ -120,7 +120,7 @@ test_that("Ground height is properly calculated", { flight.speed.kmh = 54, front.overlap = 0.5) - expect_equal( params@ground.height, ground.height, tolerance = TOLERANCE ) + expect_equal( params@ground.height, ground.height, tolerance = 1e-4 ) }) From 2772fd8dd3dd8144bec63b2de5bd36ed86674163 Mon Sep 17 00:00:00 2001 From: gsapijaszko Date: Tue, 15 Oct 2024 21:49:59 +0200 Subject: [PATCH 35/35] readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0868534..444f4d0 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ litchi_sf(roi, ## Usage There are two main functions available: * `flight.parameters()`: this will calculate the flight parameters given desired settings for GSD/height, target overlap, flight speed and camera specifications. - * `litchi.plan()`: it depends on the `flight.parameters()` return object to generate the CSV flight plan ready to import into the Litchi Hub. + * `litchi_sf()`: it depends on the `flight.parameters()` return object to generate the CSV flight plan ready to import into the Litchi Hub. ### flight.parameters - `height`: target flight height, default NA @@ -129,7 +129,7 @@ There are two main functions available: - `front.overlap`: desired height overlap between photos, default 0.8 - `flight.speed.kmh`: flight speed in km/h, default 54. - ### litchi.plan + ### litchi_sf - `roi`: range of interest loaded as an OGR layer, must be in a metric units projection for working properly - `output`: output path for the csv file @@ -200,7 +200,7 @@ exampleBoundary = readOGR( output = "output.csv" # Create the csv plan -litchi.plan(exampleBoundary, +litchi_sf(exampleBoundary, output, params, flight.lines.angle = -1,