├── CODEOWNERS ├── docs ├── requirements.txt ├── source │ ├── installation.rst │ ├── index.rst │ ├── quickstart.rst │ ├── api.rst │ ├── examples.rst │ └── contributing.rst └── Makefile ├── data └── movement.rda ├── examples ├── flowmap.png ├── map3d-rgl.png └── mobility3d.png ├── tests ├── testthat.R └── testthat │ └── test-native-c.R ├── .readthedocs.yml ├── src ├── order.h ├── Makevars ├── order.c ├── Makevars.win └── flowmap.c ├── man ├── deg2rad.Rd ├── rad2deg.Rd ├── standardize.Rd ├── RMSE.Rd ├── hour2date.Rd ├── hour2tod.Rd ├── hour2tow.Rd ├── cart2geo.radian.Rd ├── geo2cart.radian.Rd ├── dq.traj2.Rd ├── euc.dist.Rd ├── Rcolors.Rd ├── rot90.Rd ├── vbin.range.Rd ├── geo2cart.Rd ├── in.area.Rd ├── rep_each.Rd ├── seq_collapsed.Rd ├── cart2geo.Rd ├── heatmap.levels.Rd ├── voronoi2polygons.Rd ├── traj3d.close.Rd ├── melt_time.Rd ├── movr.Rd ├── seq_distinct.Rd ├── lonlat2xy.Rd ├── seq_approximate.Rd ├── cal_place_dwelling.Rd ├── minor.ticks.axis.Rd ├── movement.Rd ├── standardize_st.Rd ├── fit.polyexp.Rd ├── pairwise.dist.Rd ├── midpoint.Rd ├── vbin.Rd ├── voronoi3d.Rd ├── spatial.corr.Rd ├── fit.power.law.Rd ├── entropy.space.Rd ├── people.occurrence.Rd ├── plot_traj_graph.Rd ├── flow.stat.Rd ├── fit.truncated.power.law.Rd ├── radius.of.gyration.Rd ├── vbin.grid.Rd ├── entropy.rand.Rd ├── entropy.spacetime.Rd ├── flowmap.Rd ├── flowmap2.Rd ├── gcd.Rd ├── dq.point2.Rd ├── gen.sessions.Rd ├── point.coverage.Rd ├── plot.heatmap.Rd ├── dq.traj.Rd ├── dq.iovan.Rd ├── plot_traj3d.Rd ├── map3d.Rd ├── dq.point.Rd ├── stcoords.Rd └── plot_flowmap.Rd ├── R ├── data.R ├── movr.r ├── radius_of_gyration.R ├── heatmap.R ├── stcorr.R ├── predictability.R ├── fitdists.R ├── great_circle_distance.R ├── rgl_layers.R ├── coords.R ├── plot_mobility.R └── seq.R ├── .cursor └── rules │ └── core.mcd ├── inst └── WORDLIST ├── .gitignore ├── INSTALL ├── DESCRIPTION ├── LICENSE ├── configure ├── NAMESPACE ├── .Rbuildignore ├── CMakeLists.txt ├── cmake ├── FindR.cmake └── FindGLIB.cmake ├── scripts ├── render_docs.R ├── check_cran.sh ├── release.sh └── README.md ├── .github └── workflows │ └── cran-check.yml └── configure.win /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @caesar0301 2 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx_rtd_theme -------------------------------------------------------------------------------- /data/movement.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caesar0301/movr/HEAD/data/movement.rda -------------------------------------------------------------------------------- /examples/flowmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caesar0301/movr/HEAD/examples/flowmap.png -------------------------------------------------------------------------------- /examples/map3d-rgl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caesar0301/movr/HEAD/examples/map3d-rgl.png -------------------------------------------------------------------------------- /examples/mobility3d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caesar0301/movr/HEAD/examples/mobility3d.png -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | if (requireNamespace("testthat", quietly = TRUE)) { 2 | testthat::test_check("movr") 3 | } -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.11" 7 | 8 | python: 9 | install: 10 | - requirements: docs/requirements.txt 11 | 12 | sphinx: 13 | configuration: docs/source/conf.py 14 | 15 | formats: 16 | - pdf 17 | - epub -------------------------------------------------------------------------------- /src/order.h: -------------------------------------------------------------------------------- 1 | #ifndef __MOVR_ORDER_H__ 2 | #define __MOVR_ORDER_H__ 3 | 4 | int cmpInt(const void *v1, const void *v2); 5 | int cmpDouble(const void *v1, const void *v2); 6 | int cmpFloat(const void *v1, const void *v2); 7 | void order(void *, size_t, size_t, int (*cmp)(const void *, const void *), size_t *); 8 | 9 | #endif /* __MOVR_ORDER_H__ */ 10 | -------------------------------------------------------------------------------- /man/deg2rad.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/coords.R 3 | \name{deg2rad} 4 | \alias{deg2rad} 5 | \title{Convert degrees to radians.} 6 | \usage{ 7 | deg2rad(deg) 8 | } 9 | \arguments{ 10 | \item{deg}{A number or vector of degrees.} 11 | } 12 | \description{ 13 | Convert degrees to radians. 14 | } 15 | \seealso{ 16 | \code{\link{rad2deg}} 17 | } 18 | -------------------------------------------------------------------------------- /man/rad2deg.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/coords.R 3 | \name{rad2deg} 4 | \alias{rad2deg} 5 | \title{Convert radians to degrees.} 6 | \usage{ 7 | rad2deg(rad) 8 | } 9 | \arguments{ 10 | \item{rad}{A number or vector of radians} 11 | } 12 | \description{ 13 | Convert radians to degrees. 14 | } 15 | \seealso{ 16 | \code{\link{deg2rad}} 17 | } 18 | -------------------------------------------------------------------------------- /man/standardize.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/seq.R 3 | \name{standardize} 4 | \alias{standardize} 5 | \title{Vector Normalization} 6 | \usage{ 7 | standardize(x) 8 | } 9 | \arguments{ 10 | \item{x}{A vector to be normalized.} 11 | } 12 | \description{ 13 | Normalize a given vector. 14 | } 15 | \examples{ 16 | standardize(c(1,2,3,4,5,6)) 17 | } 18 | \seealso{ 19 | \code{\link{standardize_st}} 20 | } 21 | -------------------------------------------------------------------------------- /man/RMSE.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{RMSE} 4 | \alias{RMSE} 5 | \title{Root Mean Squared Error (RMSE)} 6 | \usage{ 7 | RMSE(x, y) 8 | } 9 | \arguments{ 10 | \item{x}{A given vector to calculate RMSE.} 11 | 12 | \item{y}{The target vector} 13 | } 14 | \description{ 15 | calculate the root mean squared error (RMSE) of two vectors. 16 | } 17 | \examples{ 18 | RMSE(c(1,2,3,4), c(2,3,2,3)) 19 | } 20 | -------------------------------------------------------------------------------- /man/hour2date.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{hour2date} 4 | \alias{hour2date} 5 | \title{Converting UNIX hour to Data object} 6 | \usage{ 7 | hour2date(hour, tz = "Asia/Shanghai") 8 | } 9 | \arguments{ 10 | \item{hour}{Hours from UNIX epoch} 11 | 12 | \item{tz}{The time zone string} 13 | } 14 | \description{ 15 | Convert UNIX hour (calculated by dividing UNIX seconds with 3600) 16 | to date at local time zone. 17 | } 18 | -------------------------------------------------------------------------------- /man/hour2tod.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{hour2tod} 4 | \alias{hour2tod} 5 | \title{Converting UNIX hour to Time-of-Day} 6 | \usage{ 7 | hour2tod(hour, tz = "Asia/Shanghai") 8 | } 9 | \arguments{ 10 | \item{hour}{Hours from UNIX epoch} 11 | 12 | \item{tz}{The time zone string} 13 | } 14 | \description{ 15 | Convert UNIX hour (calculated by dividing UNIX seconds with 3600) 16 | to to Time-of-Day (TOD) at local time zone. 17 | } 18 | -------------------------------------------------------------------------------- /man/hour2tow.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{hour2tow} 4 | \alias{hour2tow} 5 | \title{Converting UNIX hour to Time-of-Week} 6 | \usage{ 7 | hour2tow(hour, tz = "Asia/Shanghai") 8 | } 9 | \arguments{ 10 | \item{hour}{Hours from UNIX epoch} 11 | 12 | \item{tz}{The time zone string} 13 | } 14 | \description{ 15 | Convert UNIX hour (calculated by dividing UNIX seconds with 3600) 16 | to to Time-of-Week (TOW) at local time zone. 17 | } 18 | -------------------------------------------------------------------------------- /man/cart2geo.radian.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/coords.R 3 | \name{cart2geo.radian} 4 | \alias{cart2geo.radian} 5 | \title{Convert Cartesian coordinates to geopoints in radians.} 6 | \usage{ 7 | cart2geo.radian(x) 8 | } 9 | \arguments{ 10 | \item{x}{A 2D vector (lat, long) representing the geo-point in radians.} 11 | } 12 | \description{ 13 | Convert Cartesian coordinates to geopoints in radians. 14 | } 15 | \seealso{ 16 | \code{\link{geo2cart.radian}} 17 | } 18 | -------------------------------------------------------------------------------- /man/geo2cart.radian.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/coords.R 3 | \name{geo2cart.radian} 4 | \alias{geo2cart.radian} 5 | \title{Convert geopoints in radians to Cartesian coordinates.} 6 | \usage{ 7 | geo2cart.radian(x) 8 | } 9 | \arguments{ 10 | \item{x}{A 2D vector (lat, long) representing the geo-point in radians.} 11 | } 12 | \description{ 13 | Convert geopoints in radians to Cartesian coordinates. 14 | } 15 | \seealso{ 16 | \code{\link{cart2geo.radian}} 17 | } 18 | -------------------------------------------------------------------------------- /man/dq.traj2.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data_quality.R 3 | \name{dq.traj2} 4 | \alias{dq.traj2} 5 | \title{Spatiotemporal data quality of user trajectory} 6 | \usage{ 7 | dq.traj2(dqPoints) 8 | } 9 | \arguments{ 10 | \item{dqPoints}{the data quality returned by \code{\link{dq.point}}} 11 | } 12 | \value{ 13 | as the return of \code{\link{dq.traj}} 14 | } 15 | \description{ 16 | The inner implementation of algorithm in \code{\link{dq.traj}}. 17 | } 18 | \seealso{ 19 | \code{\link{dq.traj}} 20 | } 21 | -------------------------------------------------------------------------------- /man/euc.dist.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{euc.dist} 4 | \alias{euc.dist} 5 | \title{Euclidean distance between two points} 6 | \usage{ 7 | euc.dist(x1, x2) 8 | } 9 | \arguments{ 10 | \item{x1}{First point as a numeric vector} 11 | 12 | \item{x2}{Second point as a numeric vector} 13 | } 14 | \value{ 15 | The Euclidean distance between the two points 16 | } 17 | \description{ 18 | Calculate the Euclidean distance between two points in any dimension. 19 | } 20 | \examples{ 21 | euc.dist(c(0,0), c(3,4)) 22 | } 23 | -------------------------------------------------------------------------------- /man/Rcolors.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{Rcolors} 4 | \alias{Rcolors} 5 | \alias{Rcolours} 6 | \title{R Colors} 7 | \usage{ 8 | Rcolors(huesort=TRUE) 9 | } 10 | \arguments{ 11 | \item{huesort}{Boolean value to control ordering by HUE.} 12 | } 13 | \description{ 14 | Plot matrix of R colors, in index order, 25 per row. 15 | This is for quick reference when programming. 16 | } 17 | \details{ 18 | Copyright: Earl F. Glynn 19 | } 20 | \references{ 21 | http://research.stowers-institute.org/efg/R/Color/Chart/ 22 | } 23 | -------------------------------------------------------------------------------- /man/rot90.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{rot90} 4 | \alias{rot90} 5 | \title{Rotate a matrix by 90 degrees} 6 | \usage{ 7 | rot90(m) 8 | } 9 | \arguments{ 10 | \item{m}{A matrix to rotate} 11 | } 12 | \value{ 13 | The rotated matrix 14 | } 15 | \description{ 16 | Rotate a matrix by 90 degrees clockwise. 17 | Rotate matrix 90 degrees clockwise 18 | } 19 | \details{ 20 | Rotate a matrix 90 degrees clockwise by transposing and reversing columns. 21 | } 22 | \examples{ 23 | m <- matrix(1:9, nrow=3) 24 | rot90(m) 25 | } 26 | -------------------------------------------------------------------------------- /man/vbin.range.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/seq.R 3 | \name{vbin.range} 4 | \alias{vbin.range} 5 | \title{Vector range binning} 6 | \usage{ 7 | vbin.range(x, n) 8 | } 9 | \arguments{ 10 | \item{x}{a numeric vector} 11 | 12 | \item{n}{the number of bins} 13 | } 14 | \value{ 15 | the center of each interval 16 | } 17 | \description{ 18 | Bin the range of given vector into n intervals. 19 | } 20 | \examples{ 21 | vbin.range(10:20, 3) 22 | } 23 | \seealso{ 24 | \code{\link{seq_approximate}}, \code{\link{vbin}}, \code{\link{vbin.grid}} 25 | } 26 | -------------------------------------------------------------------------------- /src/Makevars: -------------------------------------------------------------------------------- 1 | # GLib include and library paths for Linux and macOS 2 | # Use pkg-config to get the correct paths for the current system 3 | # Note: Windows builds use src/Makevars.win instead 4 | 5 | # Use pkg-config if available (works on most Linux/macOS systems) 6 | PKG_CPPFLAGS = `pkg-config --cflags glib-2.0 2>/dev/null || echo "-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -I/opt/homebrew/include/glib-2.0 -I/opt/homebrew/lib/glib-2.0/include"` 7 | PKG_LIBS = `pkg-config --libs glib-2.0 2>/dev/null || echo "-lglib-2.0"` 8 | -------------------------------------------------------------------------------- /man/geo2cart.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/coords.R 3 | \name{geo2cart} 4 | \alias{geo2cart} 5 | \title{Geopoint and Cartesian conversion} 6 | \usage{ 7 | geo2cart(x) 8 | } 9 | \arguments{ 10 | \item{x}{A 2D vector (lat, long) representing the geo-point in degrees.} 11 | } 12 | \value{ 13 | A unit-length 3D vector (x, y, z) in Cartesian system. 14 | } 15 | \description{ 16 | Converting geo-points in lat/long into Cartesian coordinates. 17 | } 18 | \examples{ 19 | geo2cart(c(30, 120)) 20 | } 21 | \seealso{ 22 | \code{\link{geo2cart.radian}}, \code{\link{cart2geo}} 23 | } 24 | -------------------------------------------------------------------------------- /R/data.R: -------------------------------------------------------------------------------- 1 | #' Human mobility dataset 2 | #' 3 | #' A sample dataset containing human mobility data with spatiotemporal coordinates. 4 | #' 5 | #' @format A data frame with 7509 rows and 4 variables: 6 | #' \describe{ 7 | #' \item{id}{User identifier} 8 | #' \item{lon}{Longitude coordinate} 9 | #' \item{lat}{Latitude coordinate} 10 | #' \item{time}{Timestamp in seconds since UNIX epoch} 11 | #' \item{loc}{Location identifier} 12 | #' } 13 | #' 14 | #' @source Sample mobility data for demonstration purposes 15 | #' @docType data 16 | #' @examples 17 | #' data(movement) 18 | #' head(movement) 19 | #' summary(movement) 20 | "movement" -------------------------------------------------------------------------------- /man/in.area.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{in.area} 4 | \alias{in.area} 5 | \title{Geographic area checking} 6 | \usage{ 7 | in.area(lon, lat, area) 8 | } 9 | \arguments{ 10 | \item{lon, lat}{The point to be checked.} 11 | 12 | \item{area}{The area defined by two points c(lon1, lat1, lon2, lat2).} 13 | } 14 | \description{ 15 | Check if the given lon-lat pair falls into specific area. 16 | The area is a 4-length vector with lon-lat pairs of two points that 17 | confine the area boundaries. 18 | } 19 | \examples{ 20 | in.area(120.1, 30.1, c(120.0,30.0,120.5,30.5)) 21 | } 22 | -------------------------------------------------------------------------------- /man/rep_each.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/seq.R 3 | \name{rep_each} 4 | \alias{rep_each} 5 | \title{Replicate elements of vector} 6 | \usage{ 7 | rep_each(x, times = 2) 8 | } 9 | \arguments{ 10 | \item{x}{a vector} 11 | 12 | \item{times}{the number of replication times of each element.} 13 | } 14 | \description{ 15 | This is a slight modification of \code{rep} in basic package. It replicates 16 | each element of a vector one by one to construct a new vector. 17 | } 18 | \examples{ 19 | rep(1:10, 2) 20 | rep_each(1:10, 2) 21 | } 22 | \seealso{ 23 | \code{\link{seq_approximate}}, 24 | } 25 | -------------------------------------------------------------------------------- /man/seq_collapsed.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/seq.R 3 | \name{seq_collapsed} 4 | \alias{seq_collapsed} 5 | \title{Sequencing by collapsing adjacent same values} 6 | \usage{ 7 | seq_collapsed(v) 8 | } 9 | \arguments{ 10 | \item{v}{The input vector.} 11 | } 12 | \description{ 13 | Generate integer sequence by assigning the same adjacent values to the same 14 | level. 15 | } 16 | \examples{ 17 | seq_collapsed(c(1,2,2,3,2,2)) 18 | } 19 | \seealso{ 20 | \code{\link{seq_along}}, \code{\link{seq_distinct}}, 21 | \code{\link{vbin}}, \code{\link{vbin.range}}, \code{\link{vbin.grid}} 22 | } 23 | -------------------------------------------------------------------------------- /man/cart2geo.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/coords.R 3 | \name{cart2geo} 4 | \alias{cart2geo} 5 | \title{Cartesian and geopoint conversion} 6 | \usage{ 7 | cart2geo(x) 8 | } 9 | \arguments{ 10 | \item{x}{A unit-length 3D vector (x, y, z) in Cartesian system.} 11 | } 12 | \value{ 13 | A 2D vector (lat, long) representing the geo-point in degree. 14 | } 15 | \description{ 16 | Converting Cartesian coordinates into long/lat geo-points. 17 | } 18 | \examples{ 19 | cart2geo(c(-0.4330127, 0.7500000, 0.5000000)) 20 | } 21 | \seealso{ 22 | \code{\link{geo2cart}}, \code{\link{cart2geo.radian}} 23 | } 24 | -------------------------------------------------------------------------------- /man/heatmap.levels.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/heatmap.R 3 | \name{heatmap.levels} 4 | \alias{heatmap.levels} 5 | \title{Generate color breaks for heatmap} 6 | \usage{ 7 | heatmap.levels(z, nlevels = 10) 8 | } 9 | \arguments{ 10 | \item{z}{A numeric vector of values to classify} 11 | 12 | \item{nlevels}{The number of levels/breaks to generate} 13 | } 14 | \value{ 15 | A numeric vector of break points 16 | } 17 | \description{ 18 | Generate color breaks for heatmap visualization using equal interval classification. 19 | } 20 | \examples{ 21 | heatmap.levels(rnorm(100), nlevels=10) 22 | } 23 | -------------------------------------------------------------------------------- /man/voronoi2polygons.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{voronoi2polygons} 4 | \alias{voronoi2polygons} 5 | \title{Voronoi to polygon} 6 | \usage{ 7 | voronoi2polygons(x, poly) 8 | } 9 | \arguments{ 10 | \item{x}{A spatial object or matrix of coordinates} 11 | 12 | \item{poly}{A polygon defining the boundary} 13 | } 14 | \value{ 15 | A SpatialPolygonsDataFrame object 16 | } 17 | \description{ 18 | Convert Voronoi diagram generated by `deldir` package into SpatialPolygons 19 | http://stackoverflow.com/questions/12156475/combine-voronoi-polygons-and-maps/12159863#12159863 20 | } 21 | -------------------------------------------------------------------------------- /man/traj3d.close.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_mobility.R 3 | \name{traj3d.close} 4 | \alias{traj3d.close} 5 | \title{Close RGL 3D device} 6 | \usage{ 7 | traj3d.close() 8 | } 9 | \description{ 10 | Close the current RGL 3D device. This is a convenience function 11 | that checks if the rgl package is available before closing. 12 | } 13 | \note{ 14 | This function requires the \pkg{rgl} package to be installed. 15 | You can install it with \code{install.packages("rgl")}. 16 | } 17 | \examples{ 18 | \dontrun{ 19 | # After creating a 3D plot with plot_traj3d 20 | traj3d.close() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /man/melt_time.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{melt_time} 4 | \alias{melt_time} 5 | \title{Melt time into parts} 6 | \usage{ 7 | melt_time(epoch, tz = "Asia/Shanghai") 8 | } 9 | \arguments{ 10 | \item{epoch}{the UNIX epoch timestamp in seconds} 11 | 12 | \item{tz}{the time zone string} 13 | } 14 | \value{ 15 | several fields (indexed by order) of given timestamp: 16 | year, month, day, hour, minute, second, 17 | day of week (dow), 18 | day of year (doy), 19 | week of month (wom), 20 | week of year (woy), 21 | quarter of year (qoy) 22 | } 23 | \description{ 24 | Melt time into parts 25 | } 26 | -------------------------------------------------------------------------------- /man/movr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/movr.r 3 | \docType{package} 4 | \name{movr} 5 | \alias{movr-package} 6 | \alias{movr} 7 | \title{movr: inspecting human mobility with R} 8 | \description{ 9 | A package targeting at analyzing, modeling, and visualizing 10 | human mobility from temporal and spatial perspectives. 11 | } 12 | \seealso{ 13 | Useful links: 14 | \itemize{ 15 | \item \url{https://github.com/caesar0301/movr} 16 | \item Report bugs at \url{https://github.com/caesar0301/movr/issues} 17 | } 18 | 19 | } 20 | \author{ 21 | \strong{Maintainer}: Xiaming Chen \email{chenxm35@gmail.com} 22 | 23 | } 24 | -------------------------------------------------------------------------------- /man/seq_distinct.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/seq.R 3 | \name{seq_distinct} 4 | \alias{seq_distinct} 5 | \title{Sequencing by distinct values} 6 | \usage{ 7 | seq_distinct(v) 8 | } 9 | \arguments{ 10 | \item{v}{A vector to generate integer sequence} 11 | } 12 | \description{ 13 | Generate a new (integer) sequence according to distinct value levels. 14 | The same value takes a unique order number. 15 | } 16 | \examples{ 17 | seq_along(c(1,2,3,2)) 18 | seq_distinct(c(1,2,3,2)) 19 | } 20 | \seealso{ 21 | \code{\link{seq_along}}, \code{\link{seq_collapsed}}, 22 | \code{\link{vbin}}, \code{\link{vbin.range}}, \code{\link{vbin.grid}} 23 | } 24 | -------------------------------------------------------------------------------- /man/lonlat2xy.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{lonlat2xy} 4 | \alias{lonlat2xy} 5 | \title{Convert longitude/latitude to x/y coordinates} 6 | \usage{ 7 | lonlat2xy(lon, lat) 8 | } 9 | \arguments{ 10 | \item{lon}{Vector of longitude coordinates} 11 | 12 | \item{lat}{Vector of latitude coordinates} 13 | } 14 | \value{ 15 | A data frame with x and y columns representing local coordinates 16 | } 17 | \description{ 18 | Convert geographic coordinates (longitude/latitude) to local x/y coordinates 19 | using great circle distances from the minimum longitude and latitude points. 20 | } 21 | \examples{ 22 | lonlat2xy(c(120, 120.1, 120.2), c(30, 30.1, 30.2)) 23 | } 24 | -------------------------------------------------------------------------------- /man/seq_approximate.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/seq.R 3 | \name{seq_approximate} 4 | \alias{seq_approximate} 5 | \title{Approximately matching sequence} 6 | \usage{ 7 | seq_approximate(x, y) 8 | } 9 | \arguments{ 10 | \item{x}{A given vector to be matched} 11 | 12 | \item{y}{A target vector to calculate absolute approximation} 13 | } 14 | \description{ 15 | Match x to y approximately, and return the index of y, 16 | which is mostly near to each value in x. 17 | A variate of match() or %in% 18 | } 19 | \examples{ 20 | a <- c(1,2,3) 21 | b <- c(0.1, 0.2, 0.5) 22 | seq_approximate(a, b) 23 | } 24 | \seealso{ 25 | \code{\link{seq_along}}, \code{\link{rep_each}} 26 | } 27 | -------------------------------------------------------------------------------- /man/cal_place_dwelling.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_mobility.R 3 | \name{cal_place_dwelling} 4 | \alias{cal_place_dwelling} 5 | \title{Calculate dwelling period by averaging dwelling 6 | time between different consecutive locations} 7 | \usage{ 8 | cal_place_dwelling(loc, time) 9 | } 10 | \arguments{ 11 | \item{loc}{A vector of location identifiers} 12 | 13 | \item{time}{A vector of timestamps corresponding to each location} 14 | } 15 | \value{ 16 | A data frame with columns 'loc' and 'dwelling' containing the total dwelling time for each location 17 | } 18 | \description{ 19 | Calculate dwelling period by averaging dwelling 20 | time between different consecutive locations 21 | } 22 | -------------------------------------------------------------------------------- /.cursor/rules/core.mcd: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: true 5 | --- 6 | 7 | You're working on **movr**—an Rlang library to process and visualize human mobility spatiotemporal data. 8 | 9 | ## CODING STYLE 10 | 11 | * Keep consistent with Tidyverse Rlang style guide at https://style.tidyverse.org 12 | * We target release on CRAN when running on Linux, MacOS and Windows. 13 | * Add all dependencies of movr as Imports in DESCRIPTION. 14 | * Use importFrom instead of import in NAMESPACE file. 15 | * DO NOT edit NAMESPACE file manully. Always update it with roxygen2 or helper script. 16 | * DO NOT edit manuals under man folder manully. Always update it roxygen2 or helper script. 17 | * Always R CMD check against built package instead of project diretory. -------------------------------------------------------------------------------- /R/movr.r: -------------------------------------------------------------------------------- 1 | #' movr: inspecting human mobility with R 2 | #' 3 | #' A package targeting at analyzing, modeling, and visualizing 4 | #' human mobility from temporal and spatial perspectives. 5 | #' 6 | #' @name movr 7 | #' @docType package 8 | #' @useDynLib movr 9 | #' 10 | #' @importFrom dplyr group_by summarise mutate filter left_join distinct do select .data id 11 | #' @importFrom tidyr unite separate 12 | #' @importFrom magrittr %>% 13 | #' @importFrom igraph graph.data.frame V E optimal.community layout.kamada.kawai 14 | #' @importFrom graphics plot lines par axis rect text title filled.contour curve 15 | #' @importFrom grDevices col2rgb colorRampPalette colors rainbow rgb2hsv 16 | #' @importFrom stats cor median var 17 | #' @importFrom methods .hasSlot 18 | "_PACKAGE" 19 | -------------------------------------------------------------------------------- /man/minor.ticks.axis.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{minor.ticks.axis} 4 | \alias{minor.ticks.axis} 5 | \title{Adding minor ticks to R basic plots} 6 | \usage{ 7 | minor.ticks.axis(ax, n, lab = TRUE, tick.ratio = 0.5, mn, mx, ...) 8 | } 9 | \arguments{ 10 | \item{ax}{Which axis to add minor ticks} 11 | 12 | \item{n}{The number of minor ticks in each segment} 13 | 14 | \item{lab}{If show minor ticks' labels} 15 | 16 | \item{tick.ratio}{Ratio of minor ticks' marks} 17 | 18 | \item{mn, mx}{The min and max value of minor ticks} 19 | 20 | \item{...}{Other parameters for axis() function} 21 | } 22 | \description{ 23 | Add minor ticks for log-log plots based on R basic 24 | graphic engine. 25 | } 26 | \concept{graphic extensions} 27 | -------------------------------------------------------------------------------- /man/movement.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{movement} 5 | \alias{movement} 6 | \title{Human mobility dataset} 7 | \format{ 8 | A data frame with 7509 rows and 4 variables: 9 | \describe{ 10 | \item{id}{User identifier} 11 | \item{lon}{Longitude coordinate} 12 | \item{lat}{Latitude coordinate} 13 | \item{time}{Timestamp in seconds since UNIX epoch} 14 | \item{loc}{Location identifier} 15 | } 16 | } 17 | \source{ 18 | Sample mobility data for demonstration purposes 19 | } 20 | \usage{ 21 | movement 22 | } 23 | \description{ 24 | A sample dataset containing human mobility data with spatiotemporal coordinates. 25 | } 26 | \examples{ 27 | data(movement) 28 | head(movement) 29 | summary(movement) 30 | } 31 | \keyword{datasets} 32 | -------------------------------------------------------------------------------- /man/standardize_st.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/seq.R 3 | \name{standardize_st} 4 | \alias{standardize_st} 5 | \title{Normalization over spatial and temporal scale} 6 | \usage{ 7 | standardize_st(scoord, tcoord, value, alpha = 0.5) 8 | } 9 | \arguments{ 10 | \item{scoord}{a 1D vector of spatial coordinate} 11 | 12 | \item{tcoord}{a 1D vector of temporal coordinate} 13 | 14 | \item{value}{a value vector for each (scoord, tcoord)} 15 | 16 | \item{alpha}{a tuning parameter controlling the weight of space and time} 17 | } 18 | \description{ 19 | Scale the value along spatial and temporal coordinates simultaneously. 20 | } 21 | \examples{ 22 | scoord <- rep(seq(6), 2) 23 | tcoord <- rep(c(1,2), each=6) 24 | value <- runif(6 * 2) 25 | standardize_st(scoord, tcoord, log10(1+value)) 26 | } 27 | -------------------------------------------------------------------------------- /man/fit.polyexp.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fitdists.R 3 | \name{fit.polyexp} 4 | \alias{fit.polyexp} 5 | \title{Fit a poly-exponential distribution} 6 | \usage{ 7 | fit.polyexp(x, y, xmin = min(x), xmax = max(x), plot = TRUE, add = TRUE, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A vector of independent variable.} 11 | 12 | \item{y}{A vector of dependent variable.} 13 | 14 | \item{xmin}{The lower bound point of x.} 15 | 16 | \item{xmax}{The higher truncated point of x.} 17 | 18 | \item{plot}{Whether to plot the fitted curve.} 19 | 20 | \item{add}{Whether to add the fitted curve to current plot.} 21 | 22 | \item{...}{Extra parameters to \code{\link{curve}}.} 23 | } 24 | \value{ 25 | A list of values for a, b, c, and d. 26 | } 27 | \description{ 28 | Model: y ~ exp(a*x^2 + b*x + c) * x^d 29 | } 30 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | Barabasi 2 | GCD 3 | Geopoint 4 | Glynn 5 | Haversine 6 | Hidalgo 7 | Iovan 8 | Iovan's 9 | OSM 10 | OpenStreetMap 11 | RColorBrewer 12 | RGL 13 | SpatialPolygons 14 | SpatialPolygonsDataFrame 15 | Spatiotemporal 16 | Vincenty 17 | Voronoi 18 | al 19 | bined 20 | colorRampPalette 21 | coverages 22 | deldir 23 | doi 24 | dow 25 | doy 26 | dq 27 | efg 28 | eg 29 | entropic 30 | et 31 | etime 32 | flowmap 33 | flowmaps 34 | geo 35 | geopoint 36 | geopoints 37 | http 38 | https 39 | ie 40 | kendall 41 | loc 42 | lon 43 | lonlat 44 | md 45 | openmap 46 | pearson 47 | qoy 48 | rgl 49 | scoord 50 | slc 51 | spatio 52 | spatiotemporal 53 | spearman 54 | stackoverflow 55 | stime 56 | stowers 57 | subjectId 58 | tcoord 59 | tesselation 60 | uid 61 | vif 62 | voronoi 63 | wom 64 | woy 65 | xy 66 | xyz 67 | CMake 68 | CMD 69 | GLib 70 | natively 71 | WSL 72 | MSYS 73 | Rtools -------------------------------------------------------------------------------- /man/pairwise.dist.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/stcorr.R 3 | \name{pairwise.dist} 4 | \alias{pairwise.dist} 5 | \title{Calculate pairwise distances in a 2D region} 6 | \usage{ 7 | pairwise.dist(x, y, z) 8 | } 9 | \arguments{ 10 | \item{x, y}{the coordinates of given random field} 11 | 12 | \item{z}{the observation value for each point (x,y)} 13 | } 14 | \value{ 15 | the data frame of c('r', 'z1', 'z2') 16 | } 17 | \description{ 18 | Given a random field defined by (x, y) and observation value z, this function 19 | calculates the distance between each location pair and appends original observation 20 | values into the same row. The output can be exploited to calculate spatial correlations. 21 | Note that only Euclidean distance is supported merely for this time. 22 | } 23 | \seealso{ 24 | \code{\link{spatial.corr}} 25 | } 26 | -------------------------------------------------------------------------------- /man/midpoint.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{midpoint} 4 | \alias{midpoint} 5 | \title{Geographic midpoint calculation} 6 | \usage{ 7 | midpoint(lat, lon, w = rep(1, length(lat))) 8 | } 9 | \arguments{ 10 | \item{lat, lon}{The location points} 11 | 12 | \item{w}{The weighted value for each point} 13 | } 14 | \value{ 15 | The geographic midpoint in lat/lon 16 | } 17 | \description{ 18 | Calculate the midpoint given a list of locations denoted by 19 | latitude and longitude coordinates. 20 | } 21 | \examples{ 22 | lat <- c(30.2, 30, 30.5) 23 | lon <- c(120, 120.4, 120.5) 24 | 25 | # equal weight 26 | midpoint(lat, lon) 27 | 28 | # custom weight 29 | w <- c(1, 2, 1) 30 | midpoint(lat, lon, w) 31 | 32 | } 33 | \references{ 34 | \url{http://www.geomidpoint.com/calculation.html} 35 | } 36 | \seealso{ 37 | \code{\link{radius.of.gyration}} 38 | } 39 | -------------------------------------------------------------------------------- /man/vbin.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/seq.R 3 | \name{vbin} 4 | \alias{vbin} 5 | \title{Vector binning} 6 | \usage{ 7 | vbin(x, n, center = FALSE) 8 | } 9 | \arguments{ 10 | \item{x}{a numeric vector} 11 | 12 | \item{n}{the number of bins} 13 | 14 | \item{center}{indication of representing intervals as index (FALSE, default) or 15 | center points (TRUE).} 16 | } 17 | \value{ 18 | Sequence with interval index or center points. 19 | } 20 | \description{ 21 | Bin a vector into `n` intervals in regard with its value range. 22 | The vector x is split into n bins within [min(x), max(x)], 23 | and bin index is given by checking the bin [bin_min, bin_max) 24 | into which data points in x fall. 25 | } 26 | \examples{ 27 | vbin(1:10, 3) 28 | vbin(1:10, 3, TRUE) 29 | } 30 | \seealso{ 31 | \code{\link{seq_approximate}}, \code{\link{vbin.range}}, \code{\link{vbin.grid}} 32 | } 33 | -------------------------------------------------------------------------------- /man/voronoi3d.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rgl_layers.R 3 | \name{voronoi3d} 4 | \alias{voronoi3d} 5 | \title{3D voronoi canvas for RGL} 6 | \usage{ 7 | voronoi3d( 8 | x, 9 | y, 10 | group_by = NULL, 11 | col = NULL, 12 | side = "y", 13 | col.seg = "grey", 14 | lty = 1, 15 | lwd = 1 16 | ) 17 | } 18 | \arguments{ 19 | \item{x, y}{Coordinates of points} 20 | 21 | \item{group_by}{Grouping variable for points} 22 | 23 | \item{col}{Colors for each group} 24 | 25 | \item{side}{Which side to project the voronoi diagram ('x', 'y', or 'z')} 26 | 27 | \item{col.seg}{Color for line segments} 28 | 29 | \item{lty}{Line type} 30 | 31 | \item{lwd}{Line width} 32 | } 33 | \description{ 34 | 3D voronoi canvas for RGL 35 | } 36 | \note{ 37 | This function requires the \pkg{rgl} package to be installed. 38 | You can install it with \code{install.packages("rgl")}. 39 | } 40 | -------------------------------------------------------------------------------- /man/spatial.corr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/stcorr.R 3 | \name{spatial.corr} 4 | \alias{spatial.corr} 5 | \title{Calculate spatial correlation of a 2D region} 6 | \usage{ 7 | spatial.corr(x, y, z, beta = 0.05, corr.method = "pearson") 8 | } 9 | \arguments{ 10 | \item{x, y}{the coordinates of given random field} 11 | 12 | \item{z}{the observation value for each point (x,y)} 13 | 14 | \item{beta}{the step value to map distances into bins} 15 | 16 | \item{corr.method}{the name of function to calculate correlation, one of c("pearson", "kendall", "spearman")} 17 | } 18 | \value{ 19 | r distance between a pair of locations 20 | 21 | n the number of location pairs for specific distance 22 | 23 | corr the correlation value for specific distance 24 | } 25 | \description{ 26 | Calculate spatial correlation of a 2D region 27 | } 28 | \seealso{ 29 | \code{\link{pairwise.dist}} 30 | } 31 | -------------------------------------------------------------------------------- /man/fit.power.law.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fitdists.R 3 | \name{fit.power.law} 4 | \alias{fit.power.law} 5 | \title{Fit a power-law} 6 | \usage{ 7 | fit.power.law(x, y, xmin = min(x), xmax = max(x), plot = TRUE, add = TRUE, ...) 8 | } 9 | \arguments{ 10 | \item{x}{A vector of independent variable.} 11 | 12 | \item{y}{A vector of dependent variable.} 13 | 14 | \item{xmin}{The lower bound point of x.} 15 | 16 | \item{xmax}{The higher truncated point of x.} 17 | 18 | \item{plot}{Whether to plot the fitted curve.} 19 | 20 | \item{add}{Whether to add the fitted curve to current plot.} 21 | 22 | \item{...}{Extra parameters to \code{\link{curve}}.} 23 | } 24 | \value{ 25 | A list of values for a and lambda. 26 | } 27 | \description{ 28 | Model: \eqn{y \sim a \times x^{-\lambda}} 29 | } 30 | \seealso{ 31 | \code{\link[igraph]{fit_power_law}}, \code{\link{fit.truncated.power.law}} 32 | } 33 | -------------------------------------------------------------------------------- /man/entropy.space.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/predictability.R 3 | \name{entropy.space} 4 | \alias{entropy.space} 5 | \title{Mobility entropy} 6 | \usage{ 7 | entropy.space(probs) 8 | } 9 | \arguments{ 10 | \item{probs}{A vector of probability to find the person in unique locations.} 11 | } 12 | \value{ 13 | The entropy value in bits. 14 | } 15 | \description{ 16 | In Song's paper, three kinds of entropy are involved to investigate 17 | the predictability of human mobility, i.e., random entropy \eqn{S^{rand}}, 18 | spatial entropy \eqn{S^{unc}} and spatio-temporal entropy \eqn{S_i}. 19 | } 20 | \examples{ 21 | p <- c(0.1, 0.3, 0.5, 0.1) 22 | entropy.space(p) 23 | } 24 | \references{ 25 | Song, C. et al., Limits of predictability in human mobility. 26 | Science 2010, 1018. (doi:10.1126/science.1177170) 27 | } 28 | \seealso{ 29 | \code{\link{entropy.rand}}, \code{\link{entropy.spacetime}} 30 | } 31 | -------------------------------------------------------------------------------- /man/people.occurrence.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data_quality.R 3 | \name{people.occurrence} 4 | \alias{people.occurrence} 5 | \title{People occurrence at each stay point.} 6 | \usage{ 7 | people.occurrence(uid, x, y) 8 | } 9 | \arguments{ 10 | \item{uid}{a vector of user id} 11 | 12 | \item{x, y}{vectors of user's locations} 13 | } 14 | \value{ 15 | a data.frame with columns: uid, x, y, occur, occur.r 16 | 17 | occur the number of unique people detected at a stay point 18 | 19 | occur.r the normalized ratio of occur. 20 | } 21 | \description{ 22 | Calculate the identifiability of a person within a stay point's coverage. 23 | The identifiability is given via the average number of unique people present 24 | in the data. The more occurrence of people, the lower of identifiability to 25 | detect a specific person in the coverage. 26 | } 27 | \examples{ 28 | people.occurrence(movement$id, movement$lon, movement$lat) 29 | } 30 | -------------------------------------------------------------------------------- /man/plot_traj_graph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_mobility.R 3 | \name{plot_traj_graph} 4 | \alias{plot_traj_graph} 5 | \title{Visualize individual's trajectories.} 6 | \usage{ 7 | plot_traj_graph(loc, time, ...) 8 | } 9 | \arguments{ 10 | \item{loc}{A vector of location identifiers} 11 | 12 | \item{time}{A vector of timestamps corresponding to each location} 13 | 14 | \item{...}{Additional arguments passed to plot function} 15 | } 16 | \description{ 17 | This function plots a trajectory of a specific user as a weighted 18 | graph. Each node represents a stay point whose size indicates the 19 | (log) length of dwelling time. Each directed edge means the existence 20 | transition between stay points and its width indicates the (log) 21 | transition frequencies. 22 | } 23 | \examples{ 24 | data(movement) 25 | 26 | user <- subset(movement, id==20) 27 | \donttest{ 28 | plot_traj_graph(user$loc, user$time) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # R project and session files 2 | *.Rproj 3 | .Rproj.user/ 4 | .Rhistory 5 | .RData 6 | .RDataTmp 7 | .Rapp.history 8 | .Ruserdata 9 | .Rprofile 10 | .Renviron 11 | .httr-oauth 12 | 13 | # Build artifacts 14 | .Rcheck/ 15 | *.tar.gz 16 | *.bke 17 | *.utf8.md 18 | *-Ex.R 19 | /build/ 20 | _builds/ 21 | _install/ 22 | inst/doc/ 23 | docs/build 24 | movr-manual.pdf 25 | 26 | # Compiled objects and binaries 27 | src/*.o 28 | src/*.so 29 | src/*.dylib 30 | 31 | # Test and example outputs 32 | test/ 33 | test.c 34 | cache/ 35 | *_cache/ 36 | 37 | # Vignettes and knitr outputs 38 | vignettes/*.html 39 | vignettes/*.pdf 40 | *.knit.md 41 | 42 | # Version control 43 | .git/ 44 | .gitignore 45 | 46 | # Editors and tools 47 | .DS_Store 48 | .vscode/ 49 | .cursor/ 50 | 51 | # CI/CD and deployment 52 | rsconnect/ 53 | 54 | # Packrat (optional, if not using) 55 | packrat/lib*/ 56 | 57 | # PO/misc 58 | po/*~ 59 | 60 | ..Rcheck/ 61 | Rplots.pdf 62 | NAMESPACE.backup 63 | movr.Rcheck/ -------------------------------------------------------------------------------- /man/flow.stat.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/flowmap.R 3 | \name{flow.stat} 4 | \alias{flow.stat} 5 | \title{Calculate flow stat between locations} 6 | \usage{ 7 | flow.stat(loc, stime, etime, gap = 86400) 8 | } 9 | \arguments{ 10 | \item{loc}{A 1D vector to record locations of movement history} 11 | 12 | \item{stime}{The starting timestamp (SECONDS) vector of movement history} 13 | 14 | \item{etime}{The ending timestamp (SECONDS) vector of movement history} 15 | 16 | \item{gap}{The temporal idle interval} 17 | } 18 | \description{ 19 | Calculate flow stat between locations 20 | } 21 | \examples{ 22 | data(movement) 23 | 24 | user_move <- subset(movement, id==1) 25 | sessions <- gen.sessions(user_move[,c("loc", "time")]) 26 | 27 | res <- with(sessions, flow.stat(loc, stime, etime)) 28 | head(res) 29 | } 30 | \seealso{ 31 | \code{\link{gen.sessions}}, \code{\link{flowmap}}, \code{\link{flowmap2}}, 32 | \code{\link{plot_flowmap}} 33 | } 34 | -------------------------------------------------------------------------------- /man/fit.truncated.power.law.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fitdists.R 3 | \name{fit.truncated.power.law} 4 | \alias{fit.truncated.power.law} 5 | \title{Fit a truncated power-law.} 6 | \usage{ 7 | fit.truncated.power.law( 8 | x, 9 | y, 10 | xmin = min(x), 11 | xmax = max(x), 12 | plot = TRUE, 13 | add = TRUE, 14 | ... 15 | ) 16 | } 17 | \arguments{ 18 | \item{x}{A vector of independent variable.} 19 | 20 | \item{y}{A vector of dependent variable.} 21 | 22 | \item{xmin}{The lower bound point of x.} 23 | 24 | \item{xmax}{The higher truncated point of x.} 25 | 26 | \item{plot}{Whether to plot the fitted curve.} 27 | 28 | \item{add}{Whether to add the fitted curve to current plot.} 29 | 30 | \item{...}{Extra parameters to \code{\link{curve}}.} 31 | } 32 | \value{ 33 | A list of values for a, lambda and k. 34 | } 35 | \description{ 36 | Model: y ~ a * x^(-lambda) exp(-x/k) 37 | } 38 | \seealso{ 39 | \code{\link{fit.power.law}} 40 | } 41 | -------------------------------------------------------------------------------- /man/radius.of.gyration.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/radius_of_gyration.R 3 | \name{radius.of.gyration} 4 | \alias{radius.of.gyration} 5 | \title{Radius of gyration for human mobility} 6 | \usage{ 7 | radius.of.gyration(lat, lon, w = rep(1, length(lat))) 8 | } 9 | \arguments{ 10 | \item{lat, lon}{The geographic coordinates of locations} 11 | 12 | \item{w}{The weight value for each location} 13 | } 14 | \value{ 15 | The radius of gyration (km) 16 | } 17 | \description{ 18 | Given a series of locations denoted by lat/lon coordinates, 19 | the radius of gyration for individual is calculated. 20 | } 21 | \examples{ 22 | lat <- c(30.2, 30, 30.5) 23 | lon <- c(120, 120.4, 120.5) 24 | radius.of.gyration(lat, lon) 25 | 26 | } 27 | \references{ 28 | M. C. Gonzalez, C. A. Hidalgo, and A.-L. Barabasi, 29 | "Understanding individual human mobility patterns," 30 | Nature, vol. 453, no. 7196, pp. 779-782, Jun. 2008. 31 | } 32 | \seealso{ 33 | \code{\link{midpoint}} 34 | } 35 | -------------------------------------------------------------------------------- /man/vbin.grid.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/seq.R 3 | \name{vbin.grid} 4 | \alias{vbin.grid} 5 | \title{2D random field binning} 6 | \usage{ 7 | vbin.grid(x, y, z, nx, ny, FUN = mean, na = NA) 8 | } 9 | \arguments{ 10 | \item{x, y, z}{a random field with location vectors (x, y) and value vector z. 11 | They must have the same length.} 12 | 13 | \item{nx, ny}{the number of bins in x and y dimension.} 14 | 15 | \item{FUN}{a function to calculate statistics in each 2D bin.} 16 | 17 | \item{na}{Replacement for NA value in matrix bins.} 18 | } 19 | \value{ 20 | a matrix with row (column) names being the center points of x (y) dim, 21 | and with cell value being the aggregate statistics calculated by FUN. 22 | } 23 | \description{ 24 | Generate a bined matrix given a 2D random field. 25 | } 26 | \examples{ 27 | vbin.grid(1:20, 20:1, runif(20), nx=5, ny=5) 28 | } 29 | \seealso{ 30 | \code{\link{seq_approximate}}, \code{\link{vbin}}, \code{\link{vbin.range}} 31 | } 32 | -------------------------------------------------------------------------------- /man/entropy.rand.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/predictability.R 3 | \name{entropy.rand} 4 | \alias{entropy.rand} 5 | \title{Major concepts, tools and visualization in the predictability paper. 6 | Song, C. et al., Limits of predictability in human mobility. 7 | Science 2010, 1018. (doi:10.1126/science.1177170) 8 | Mobility entropy} 9 | \usage{ 10 | entropy.rand(N) 11 | } 12 | \arguments{ 13 | \item{N}{The number of unique locations.} 14 | } 15 | \value{ 16 | The entropy value in bits. 17 | } 18 | \description{ 19 | In Song's paper, three kinds of entropy are involved to investigate 20 | the predictability of human mobility, i.e., random entropy \eqn{S^{rand}}, 21 | spatial entropy \eqn{S^{unc}} and spatio-temporal entropy \eqn{S_i}. 22 | } 23 | \references{ 24 | Song, C. et al., Limits of predictability in human mobility. 25 | Science 2010, 1018. (doi:10.1126/science.1177170) 26 | } 27 | \seealso{ 28 | \code{\link{entropy.space}}, \code{\link{entropy.spacetime}} 29 | } 30 | -------------------------------------------------------------------------------- /man/entropy.spacetime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/predictability.R 3 | \name{entropy.spacetime} 4 | \alias{entropy.spacetime} 5 | \title{Mobility entropy} 6 | \usage{ 7 | entropy.spacetime(x, y = NULL) 8 | } 9 | \arguments{ 10 | \item{x, y}{A chronologically ordered sequence of visited locations. The sequence 11 | is given by a vector (e.g. location names) or two vectors of location coordinates 12 | (e.g., latitude and longitude values).} 13 | } 14 | \value{ 15 | The entropy value in bits. 16 | } 17 | \description{ 18 | In Song's paper, three kinds of entropy are involved to investigate 19 | the predictability of human mobility, i.e., random entropy \eqn{S^{rand}}, 20 | spatial entropy \eqn{S^{unc}} and spatio-temporal entropy \eqn{S_i}. 21 | } 22 | \references{ 23 | Song, C. et al., Limits of predictability in human mobility. 24 | Science 2010, 1018. (doi:10.1126/science.1177170) 25 | } 26 | \seealso{ 27 | \code{\link{entropy.rand}}, \code{\link{entropy.space}} 28 | } 29 | -------------------------------------------------------------------------------- /R/radius_of_gyration.R: -------------------------------------------------------------------------------- 1 | #' Radius of gyration for human mobility 2 | #' 3 | #' Given a series of locations denoted by lat/lon coordinates, 4 | #' the radius of gyration for individual is calculated. 5 | #' 6 | #' @param lat,lon The geographic coordinates of locations 7 | #' @param w The weight value for each location 8 | #' @return The radius of gyration (km) 9 | #' @export 10 | #' @references M. C. Gonzalez, C. A. Hidalgo, and A.-L. Barabasi, 11 | #' "Understanding individual human mobility patterns," 12 | #' Nature, vol. 453, no. 7196, pp. 779-782, Jun. 2008. 13 | #' @examples 14 | #' lat <- c(30.2, 30, 30.5) 15 | #' lon <- c(120, 120.4, 120.5) 16 | #' radius.of.gyration(lat, lon) 17 | #' 18 | #' @seealso \code{\link{midpoint}} 19 | radius.of.gyration <- function(lat, lon, w=rep(1, length(lat))) { 20 | # get the midpoint of given locations 21 | mp <- midpoint(lat, lon, w) 22 | 23 | # calculate distance from the midpoint 24 | diff <- apply(cbind(lat, lon), 1, function(x) gcd(mp, x)) 25 | 26 | sqrt(1.0 * sum(diff^2) / length(diff)) 27 | } -------------------------------------------------------------------------------- /man/flowmap.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/flowmap.R 3 | \name{flowmap} 4 | \alias{flowmap} 5 | \title{Generate flowmap from movement data} 6 | \usage{ 7 | flowmap(uid, loc, time, gap = 8 * 3600) 8 | } 9 | \arguments{ 10 | \item{uid}{a vector to record user identities} 11 | 12 | \item{loc}{a 1D vector to record locations of movement history} 13 | 14 | \item{time}{the timestamp (SECONDS) vector of movement history} 15 | 16 | \item{gap}{the maximum dwelling time to consider a valid move between locations.} 17 | } 18 | \value{ 19 | a data frame with four columns: from, to, total, unique (users) 20 | } 21 | \description{ 22 | Use historical movement data to generate flowmap, which records mobility 23 | statistics between two locations 'from' and 'to'. 24 | } 25 | \examples{ 26 | data(movement) 27 | 28 | res <- with(movement, flowmap(id, loc, time)) 29 | head(res) 30 | } 31 | \seealso{ 32 | \code{\link{gen.sessions}}, \code{\link{flowmap2}}, \code{\link{flow.stat}}, 33 | \code{\link{plot_flowmap}} 34 | } 35 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | # Install Movr 2 | 3 | ## Prerequisites 4 | 5 | On Ubuntu 6 | 7 | > sudo apt-get install cmake pkg-config libglib2.0-dev libglpk-dev libgmp-dev libopengl-dev 8 | 9 | On Mac OS X 10 | 11 | > brew install cmake pkg-config glib glpk 12 | 13 | ## Install 14 | 15 | 1. Install dependencies 16 | 17 | ```R 18 | # for pkg installation 19 | install.packages('devtools') 20 | 21 | # for movr 22 | install.packages(c('dplyr', 'tidyr', 'data.table', 'geosphere', 'deldir', 'RColorBrewer', 'igraph', 'rgl')) 23 | ``` 24 | 25 | 2. Install movr 26 | 27 | a. From github 28 | 29 | ```R 30 | library(devtools) 31 | install_github("caesar0301/movr") 32 | ``` 33 | 34 | b. From source code 35 | 36 | ```bash 37 | # Build C source 38 | ./configure 39 | 40 | # Check package compliance (recommended) 41 | ./scripts/check_cran.sh --quick 42 | 43 | # Or run basic check 44 | R CMD build . 45 | R CMD check movr_*.tar.gz 46 | 47 | # Run examples 48 | R --no-save -e "library(devtools);run_examples()" 49 | 50 | # Install 51 | R --no-save -e "library(devtools);install()" 52 | ``` 53 | -------------------------------------------------------------------------------- /man/flowmap2.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/flowmap.R 3 | \name{flowmap2} 4 | \alias{flowmap2} 5 | \title{Generate flowmap from movement data} 6 | \usage{ 7 | flowmap2(uid, loc, stime, etime, gap = 86400) 8 | } 9 | \arguments{ 10 | \item{uid}{a vector to record user identities} 11 | 12 | \item{loc}{a 1D vector to record locations of movement history} 13 | 14 | \item{stime, etime}{compressed session time at each location} 15 | 16 | \item{gap}{the maximum dwelling time to consider a valid move between locations} 17 | } 18 | \value{ 19 | a data frame with four columns: from, to, total, unique (users) 20 | } 21 | \description{ 22 | Use historical movement data to generate flowmap, which records mobility 23 | statistics between two locations 'from' and 'to'. 24 | } 25 | \details{ 26 | Different from \code{flowmap}, compressed movement history is used to 27 | generate flow statistics. 28 | } 29 | \seealso{ 30 | \code{\link{gen.sessions}}, \code{\link{flowmap}}, \code{\link{flow.stat}}, 31 | \code{\link{plot_flowmap}} 32 | } 33 | -------------------------------------------------------------------------------- /man/gcd.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/great_circle_distance.R 3 | \name{gcd} 4 | \alias{gcd} 5 | \title{Great Circle Distance (GCD)} 6 | \usage{ 7 | gcd(p1, p2, type = "hf", na = 0) 8 | } 9 | \arguments{ 10 | \item{p1}{Location of point 1 with (lat, long) coordinates.} 11 | 12 | \item{p2}{Location of point 2 with (lat, long) coordinates.} 13 | 14 | \item{type}{Specific algorithm to use, c('slc', 'hf', 'vif').} 15 | 16 | \item{na}{Value to return for NA distances} 17 | } 18 | \value{ 19 | Distance in kilometers (km). 20 | } 21 | \description{ 22 | Calculates the geodesic distance between two points specified by radian 23 | latitude/longitude using one of the Spherical Law of Cosines (slc), 24 | the Haversine formula (hf), or the Vincenty inverse formula for 25 | ellipsoids (vif). 26 | } 27 | \examples{ 28 | # Point in (lat, long) format 29 | p1 <- c(30.0, 120.0) 30 | p2 <- c(30.5, 120.5) 31 | 32 | gcd(p1, p2) 33 | gcd(p1, p2, type="hf") 34 | gcd(p1, p2, type="vif") 35 | } 36 | \references{ 37 | \url{ 38 | https://www.r-bloggers.com/2010/11/great-circle-distance-calculations-in-r/} 39 | } 40 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: movr 2 | Type: Package 3 | Version: 0.3.4 4 | Title: Analyzing and Visualizing Human Mobility Data 5 | Description: A set of functions and utilities to analyze and visualize 6 | spatiotemporal mobility data. 7 | Authors@R: c( 8 | person("Xiaming", "Chen", 9 | email = "chenxm35@gmail.com", 10 | role = c("aut", "cre")) 11 | ) 12 | URL: https://github.com/caesar0301/movr 13 | BugReports: https://github.com/caesar0301/movr/issues 14 | Language: en-US 15 | Encoding: UTF-8 16 | Depends: 17 | R (>= 3.0.0) 18 | Imports: 19 | dplyr (>= 0.7.0), 20 | tidyr (>= 0.6.1), 21 | geosphere (>= 1.5-5), 22 | deldir (>= 0.1-14), 23 | methods, 24 | parallel, 25 | magrittr, 26 | RColorBrewer, 27 | squash, 28 | igraph, 29 | sp, 30 | graphics, 31 | grDevices, 32 | stats, 33 | classInt 34 | Suggests: 35 | testthat (>= 3.0.0), 36 | OpenStreetMap, 37 | rgl 38 | LazyData: yes 39 | NeedsCompilation: yes 40 | License: MIT + file LICENSE 41 | License_is_FOSS: true 42 | SystemRequirements: GNU CMake 43 | RoxygenNote: 7.3.2 44 | Config/testthat/edition: 3 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014-2025 Xiaming Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /man/dq.point2.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data_quality.R 3 | \name{dq.point2} 4 | \alias{dq.point2} 5 | \title{Spatiotemporal data quality of stay points} 6 | \usage{ 7 | dq.point2(sessions, pc, po, dq.min = 1e-05, na = dq.min) 8 | } 9 | \arguments{ 10 | \item{sessions}{a data.frame of session data generated by \code{\link{gen.sessions}} 11 | or external tools. Four columns are required: c('stime', 'etime', 'x', 'y')} 12 | 13 | \item{pc}{the point coverage, see \code{\link{point.coverage}}} 14 | 15 | \item{po}{the occurrence of people, see \code{\link{people.occurrence}}} 16 | 17 | \item{dq.min}{the lower bound of data quality. Values less than this bound 18 | will be set by force to this quality (default 1e-5)} 19 | 20 | \item{na}{the replacement of NA (default dq.min)} 21 | } 22 | \value{ 23 | as the return of \code{\link{dq.point}} 24 | } 25 | \description{ 26 | This is the inner implementation of \code{\link{dq.point}}. Differently, 27 | this function accepts the session data generated by \code{\link{gen.sessions}}, 28 | or external utilities. 29 | } 30 | \seealso{ 31 | \code{\link{dq.point}} 32 | } 33 | -------------------------------------------------------------------------------- /man/gen.sessions.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/flowmap.R 3 | \name{gen.sessions} 4 | \alias{gen.sessions} 5 | \title{Detect staying sessions of user} 6 | \usage{ 7 | gen.sessions(x, y = NULL, t = NULL, gap = 0.5 * 3600, unite.sep = "_") 8 | } 9 | \arguments{ 10 | \item{x, y, t}{see parameters of \code{\link{stcoords}}} 11 | 12 | \item{gap}{the time tolerance (sec) to combine two continuous observations} 13 | 14 | \item{unite.sep}{a separator to combine x and y coordinators into one column, 15 | see also \code{\link{stcoords}}} 16 | } 17 | \description{ 18 | This function removes duplicate location records in user's mobility trajectory. 19 | Successive records at the same location are merged into a single session 20 | (with interval less than `gap`) recording the starting and ending times. 21 | } 22 | \examples{ 23 | data(movement) 24 | u1 <- dplyr::filter(movement, id==2) 25 | 26 | ## 1-column location indicators 27 | head(gen.sessions(u1$loc, t=u1$time)) 28 | 29 | ## 2-column location coordinates 30 | head(gen.sessions(u1$lon, u1$lat, u1$time)) 31 | } 32 | \seealso{ 33 | \code{\link{flowmap}}, \code{\link{flowmap2}}, \code{\link{flow.stat}} 34 | } 35 | -------------------------------------------------------------------------------- /man/point.coverage.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data_quality.R 3 | \name{point.coverage} 4 | \alias{point.coverage} 5 | \title{Spatial coverages of stay points.} 6 | \usage{ 7 | point.coverage(x, y, type = "lonlat") 8 | } 9 | \arguments{ 10 | \item{x, y}{the coordinates of all (unique) stay points in Euclidean space} 11 | 12 | \item{type}{the type of coordinate system: c('lonlat', 'xy')} 13 | } 14 | \value{ 15 | a data.frame with four columns: 16 | 17 | x,y input stay points 18 | 19 | area the vector of spatial area (km^2) of each mosaic in Voronoi diagram 20 | 21 | area.r the normalized ratio of log(area). 22 | } 23 | \description{ 24 | Calculate the spatial coverage of each data point by employing the Voronoi tesselation 25 | (with \code{\link[deldir]{deldir}}). The spatial area is calculated in a Euclidean 26 | space. Thus the user should convert long/lat data into Euclidean coordinates before 27 | passing into the function. 28 | } 29 | \examples{ 30 | ## Long/lat points 31 | head(point.coverage(movement$lon, movement$lat, type='lonlat')) 32 | 33 | ## Euclidean points 34 | ii <- lonlat2xy(movement$lon, movement$lat) 35 | head(point.coverage(ii$x, ii$y, type='xy')) 36 | } 37 | -------------------------------------------------------------------------------- /src/order.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "order.h" 5 | 6 | int 7 | cmpInt(const void *v1, const void *v2) { 8 | const int v1_ = **((const int **)v1); 9 | const int v2_ = **((const int **)v2); 10 | return v1_ < v2_ ? -1 : v1_ > v2_; 11 | } 12 | 13 | int 14 | cmpDouble(const void *v1, const void *v2) { 15 | const double v1_ = **((const double **)v1); 16 | const double v2_ = **((const double **)v2); 17 | return v1_ < v2_ ? -1 : v1_ > v2_; 18 | } 19 | 20 | int 21 | cmpFloat(const void *v1, const void *v2) { 22 | const float v1_ = **((const float **)v1); 23 | const float v2_ = **((const float **)v2); 24 | return v1_ < v2_ ? -1 : v1_ > v2_; 25 | } 26 | 27 | /** 28 | * Order an array and return the index sequence of odered values. 29 | */ 30 | void 31 | order(void *array, size_t nitems, size_t size, 32 | int (*cmp)(const void *p1, const void *p2), size_t *result) { 33 | char *aa = array; 34 | void *pindex[nitems]; 35 | int i; 36 | 37 | for ( i = 0; i < nitems; i++) { 38 | pindex[i] = aa + size * i; 39 | } 40 | 41 | qsort(pindex, nitems, sizeof(*pindex), cmp); 42 | 43 | for ( i = 0; i < nitems; i++ ) { 44 | result[i] = ((char *)pindex[i] - aa ) / size; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /man/plot.heatmap.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/heatmap.R 3 | \name{plot.heatmap} 4 | \alias{plot.heatmap} 5 | \title{Heatmap of xyz data} 6 | \usage{ 7 | \method{plot}{heatmap}( 8 | x, 9 | y, 10 | z, 11 | nx = 100, 12 | ny = 100, 13 | na = 0, 14 | nlevels = 10, 15 | levels = NULL, 16 | colors = rev(RColorBrewer::brewer.pal(11, "Spectral")), 17 | bias = 1, 18 | ... 19 | ) 20 | } 21 | \arguments{ 22 | \item{x, y}{the coordinates of each z value.} 23 | 24 | \item{z}{the observed value for each (x,y) point.} 25 | 26 | \item{nx, ny}{the number of bins in x and y dimension, used by \code{\link{vbin.grid}}.} 27 | 28 | \item{na}{replacement for NA value in matrix bins, used by \code{\link{vbin.grid}}.} 29 | 30 | \item{nlevels}{the number of colorful stages to plot, see `filled.contour`.} 31 | 32 | \item{levels}{a numeric vector to indicate the colorful stages, see `filled.contour`.} 33 | 34 | \item{colors}{a vector of color names or hex values, see `colorRampPalette`. Default uses RColorBrewer::brewer.pal(11, "Spectral").} 35 | 36 | \item{bias}{a positive number. Higher values give more widely spaced colors at the high end, see `colorRampPalette`.} 37 | 38 | \item{...}{other parameters sent to `filled.contour`.} 39 | } 40 | \description{ 41 | Visualize the xyz data in a 2d heatmap. 42 | } 43 | -------------------------------------------------------------------------------- /src/Makevars.win: -------------------------------------------------------------------------------- 1 | # Windows-specific build configuration for movr package 2 | 3 | # Compiler flags for Windows 4 | PKG_CPPFLAGS = -I. -DWIN32_LEAN_AND_MEAN 5 | 6 | # Try to find GLib via pkg-config first, fallback to common paths 7 | PKG_CONFIG_EXISTS = $(shell pkg-config --exists glib-2.0 && echo yes) 8 | 9 | ifeq ($(PKG_CONFIG_EXISTS), yes) 10 | # Use pkg-config if available 11 | PKG_CPPFLAGS += $(shell pkg-config --cflags glib-2.0) 12 | PKG_LIBS = $(shell pkg-config --libs glib-2.0) 13 | else 14 | # Fallback for common GLib installation paths on Windows 15 | # Users may need to install GLib for Windows (e.g., via MSYS2, vcpkg, or pre-built binaries) 16 | PKG_CPPFLAGS += -I$(GLIB_INCLUDE_PATH) -I$(GLIB_INCLUDE_PATH)/glib-2.0 -I$(GLIB_LIB_PATH)/glib-2.0/include 17 | PKG_LIBS = -L$(GLIB_LIB_PATH) -lglib-2.0 18 | endif 19 | 20 | # Ensure compatibility with different Windows compilers 21 | ifeq ($(CC), gcc) 22 | # MinGW/GCC specific flags 23 | PKG_CPPFLAGS += -std=c99 24 | PKG_LIBS += -lws2_32 25 | else 26 | # MSVC specific flags 27 | PKG_CPPFLAGS += /TC 28 | endif 29 | 30 | # Ensure the shared library is named movr.dll (not flowmap.dll) 31 | # This is handled by R CMD SHLIB -o movr.dll or during package installation 32 | 33 | # Default target 34 | all: $(SHLIB) 35 | 36 | # Clean target 37 | clean: 38 | $(RM) *.o *.dll -------------------------------------------------------------------------------- /tests/testthat/test-native-c.R: -------------------------------------------------------------------------------- 1 | context("Native C functions: _compress_mov and _flow_stat") 2 | 3 | test_that("_compress_mov compresses movement history correctly", { 4 | # Example input: locations and times 5 | loc <- as.integer(c(1, 1, 2, 2, 1, 1)) 6 | time <- as.numeric(c(0, 10, 20, 30, 40, 50)) 7 | gap <- 15 8 | # Call the C function 9 | result <- .Call("_compress_mov", loc, time, gap) 10 | # Check structure: should be a list of 3 elements 11 | expect_type(result, "list") 12 | expect_length(result, 3) 13 | # Check that compressed locations are correct 14 | expect_equal(result[[1]], as.integer(c(1, 2, 1))) 15 | # Check that start and end times are correct 16 | expect_equal(result[[2]], c(0, 20, 40)) 17 | expect_equal(result[[3]], c(10, 30, 50)) 18 | }) 19 | 20 | test_that("_flow_stat calculates flow statistics correctly", { 21 | # Example input: locations and times 22 | loc <- as.character(c("A", "A", "B", "B", "A", "A")) 23 | stime <- c(0, 20, 40) 24 | etime <- c(10, 30, 50) 25 | gap <- 15 26 | # Call the C function 27 | result <- .Call("_flow_stat", as.character(c("A", "B", "A")), stime, etime, gap) 28 | # Check structure: should be a list of 2 elements 29 | expect_type(result, "list") 30 | expect_length(result, 2) 31 | # Edges and flows 32 | expect_type(result[[1]], "character") 33 | expect_type(result[[2]], "integer") 34 | }) -------------------------------------------------------------------------------- /man/dq.traj.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data_quality.R 3 | \name{dq.traj} 4 | \alias{dq.traj} 5 | \title{Spatiotemporal data quality of user trajectory} 6 | \usage{ 7 | dq.traj(stcoords, pc, po) 8 | } 9 | \arguments{ 10 | \item{stcoords}{the spatiotemporal coordinates, see \code{\link{stcoords}}} 11 | 12 | \item{pc}{the point coverage, see \code{\link{point.coverage}}} 13 | 14 | \item{po}{the occurrence of people, see \code{\link{people.occurrence}}} 15 | } 16 | \value{ 17 | a data.frame of data qualities for each user. 18 | 19 | N the number of data points in a trajectory 20 | 21 | dq the data quality of a given trajectory 22 | 23 | mean the average data quality of stay points 24 | 25 | entropy the informative entropy of point data qualities 26 | } 27 | \description{ 28 | This function calculates the data quality of a user's trajectory. The measure 29 | is quantifies with heterogeneity of spatiotemporal data points, as obtained 30 | via a grid-based optimal searching algorithm. 31 | } 32 | \examples{ 33 | u1 <- dplyr::filter(movement, id==4) 34 | pc <- point.coverage(movement$lon, movement$lat) 35 | po <- people.occurrence(movement$id, movement$lon, movement$lat) 36 | stc <- stcoords(u1[,c('lon','lat','time')]) 37 | 38 | dq.traj(stc, pc, po) 39 | dq.traj2(dq.point(stc, pc, po)) 40 | } 41 | \seealso{ 42 | \code{\link{dq.traj2}} 43 | } 44 | -------------------------------------------------------------------------------- /man/dq.iovan.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data_quality.R 3 | \name{dq.iovan} 4 | \alias{dq.iovan} 5 | \title{Spatiotemporal data quality of Iovan's} 6 | \usage{ 7 | dq.iovan(stcoords, type = "lonlat") 8 | } 9 | \arguments{ 10 | \item{stcoords}{The spatiotemporal coordinates, see \code{\link{stcoords}}} 11 | 12 | \item{type}{the selection of distance calculation function: 'lonlat' -> great circle distance 13 | of long/lat pair, 'xy' -> euclidean distance.} 14 | } 15 | \value{ 16 | a list of data quality indicators: 17 | 18 | theta the speed index 19 | 20 | Q the unified quality indicator for each data point 21 | 22 | H the entropic quality indicator for each mobility trajectory 23 | } 24 | \description{ 25 | In this data quality measure, Iovan et al.[1] addressed mobility data quality from both 26 | local and global aspects. The local measure quantifies the quality of each data point, 27 | which encodes the reasonableness of data mainly with regards to SPEED (or time and distance). 28 | The global measure based on informational entropy quantifies the data quality within 29 | a whole trajectory. This function implements these measures, which are also referred 30 | to as the DYNAMIC quality for mobility data. 31 | } 32 | \examples{ 33 | u1 <- dplyr::filter(movement, id==1) 34 | stc <- stcoords(u1[, c('lon','lat','time')]) 35 | dq.iovan(stc, type='lonlat') 36 | } 37 | \references{ 38 | [1] https://doi.org/10.1007/978-3-319-00615-4_14 39 | } 40 | -------------------------------------------------------------------------------- /man/plot_traj3d.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot_mobility.R 3 | \name{plot_traj3d} 4 | \alias{plot_traj3d} 5 | \title{Visualize trajectories using RGL 3D.} 6 | \usage{ 7 | plot_traj3d( 8 | x, 9 | y, 10 | t, 11 | group_by = NULL, 12 | col = NULL, 13 | xlab = "", 14 | ylab = "", 15 | tlab = "", 16 | ... 17 | ) 18 | } 19 | \arguments{ 20 | \item{x, y}{Numeric vectors of spatial coordinates} 21 | 22 | \item{t}{The temporal vector for each (x,y) point.} 23 | 24 | \item{group_by}{A group indicator when multiple users are visualized.} 25 | 26 | \item{col}{A vector of color strings. It must have the same length as unique(group_by).} 27 | 28 | \item{xlab, ylab, tlab}{The labels for each axis.} 29 | 30 | \item{...}{Other parameters for \code{\link[rgl]{plot3d}} or \code{\link[rgl]{axes3d}}} 31 | } 32 | \description{ 33 | Visualize trajectories using RGL 3D. 34 | } 35 | \note{ 36 | This function requires the \pkg{rgl} package to be installed. 37 | You can install it with \code{install.packages("rgl")}. 38 | } 39 | \examples{ 40 | data(movement) 41 | 42 | users <- subset(movement, id \%in\% c(23, 20)) 43 | users <- dplyr::mutate(users, time = time/86400 - min(time/86400)) 44 | users <- dplyr::filter(users, time <= 30) 45 | \dontrun{ 46 | if(require(OpenStreetMap) && require(rgl)){ 47 | plot_traj3d(users$lon, users$lat, users$time, 48 | group_by=users$id, col=c('royalblue', 'orangered')) 49 | invisible(readline(prompt="Press [enter] to continue")) 50 | traj3d.close() 51 | }} 52 | } 53 | -------------------------------------------------------------------------------- /man/map3d.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/rgl_layers.R 3 | \name{map3d} 4 | \alias{map3d} 5 | \title{Add a 3D map surface} 6 | \usage{ 7 | map3d(lowerLeft, upperRight, h = 0, type = "bing", ...) 8 | } 9 | \arguments{ 10 | \item{lowerLeft}{the lower left lat and long} 11 | 12 | \item{upperRight}{the upper right lat and long} 13 | 14 | \item{h}{the horizontal plane to locate the map surface} 15 | 16 | \item{type}{the map type to use} 17 | 18 | \item{...}{all other parameters of \code{\link[OpenStreetMap]{openmap}}} 19 | } 20 | \description{ 21 | This method add a 3D map surface to the RGL plot. The backend map service is 22 | supported by OpenStreetMap package. All parameters except for h are 23 | consistent with the 'openmap' function in OSM. 24 | } 25 | \note{ 26 | This function requires the \pkg{rgl} package to be installed. 27 | You can install it with \code{install.packages("rgl")}. 28 | } 29 | \examples{ 30 | data(movement) 31 | 32 | u1 <- subset(movement, id==3) 33 | u1$time <- (u1$time - min(u1$time)) / 3600 34 | lat1 <- min(u1$lat) - 0.005 35 | lat2 <- max(u1$lat) + 0.005 36 | lon1 <- min(u1$lon) - 0.005 37 | lon2 <- max(u1$lon) + 0.005 38 | \dontrun{ 39 | if(require(OpenStreetMap) && require(rgl)){ 40 | library(rgl) 41 | rgl.clear() 42 | rgl.clear("lights") 43 | rgl.bg(color="lightgray") 44 | rgl.viewpoint(theta=240, phi=45) 45 | rgl.light(theta = 45, phi = 45, viewpoint.rel=TRUE) 46 | map3d(c(lat1, lon1), c(lat2, lon2), h=min(u1$time)) 47 | axes3d(edges = "bbox", labels = TRUE, tick = TRUE, nticks = 5, box=FALSE, 48 | expand = 1.03, col="black", lwd=0.8) 49 | invisible(readline(prompt="Press [enter] to continue")) 50 | rgl.close() 51 | }} 52 | } 53 | \seealso{ 54 | \code{\link[OpenStreetMap]{openmap}} 55 | } 56 | -------------------------------------------------------------------------------- /man/dq.point.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data_quality.R 3 | \name{dq.point} 4 | \alias{dq.point} 5 | \title{Spatiotemporal data quality of stay points} 6 | \usage{ 7 | dq.point(stcoords, pc, po, dq.min = 1e-05, na = dq.min) 8 | } 9 | \arguments{ 10 | \item{stcoords}{the spatiotemporal coordinates, see \code{\link{stcoords}}} 11 | 12 | \item{pc}{the point coverage, see \code{\link{point.coverage}}} 13 | 14 | \item{po}{the occurrence of people, see \code{\link{people.occurrence}}} 15 | 16 | \item{dq.min}{the lower bound of data quality. Values less than this bound 17 | will be set by force to this quality (default 1e-5)} 18 | 19 | \item{na}{the replacement of NA (default dq.min)} 20 | } 21 | \value{ 22 | the original sessions with extra fields: c('dq.s', 'dq.d', 'dq'). `dq.s` 23 | and `dq.d` give the STATIC and DYNAMIC quality measures respectively, while 24 | `dq` is the Harmonic mean of previous two. 25 | } 26 | \description{ 27 | This function gives the local quality metric of spatiotemporal data points. 28 | The metric contains two parts of quality information, ie STATIC and DYNAMIC. 29 | The STATIC part comes from the spatial and temporal coverage of each point, 30 | which quantifies the uncertainty of user whereabouts caused by the intrinsic 31 | limitation of measurement method (eg location coverage). The DYNAMIC part 32 | gives the quality from dynamic information like speed, which uses the 33 | Iovan's algorithm as addressed in \code{\link{dq.iovan}}. 34 | } 35 | \examples{ 36 | u1 <- dplyr::filter(movement, id==1) 37 | pc <- point.coverage(movement$lon, movement$lat) 38 | po <- people.occurrence(movement$id, movement$lon, movement$lat) 39 | 40 | stc <- stcoords(u1[,c('lon','lat','time')]) 41 | head(dq.point(stc, pc, po)) 42 | 43 | sessions <- gen.sessions(stc$x, stc$y, stc$t) 44 | head(dq.point2(sessions, pc, po)) 45 | } 46 | \seealso{ 47 | \code{\link{dq.point2}} 48 | } 49 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | PKG_ROOT="$(cd "$(dirname "$0")"; pwd)" 3 | 4 | echo "=== movr Package Build Configuration ===" 5 | echo "Package root: ${PKG_ROOT}" 6 | 7 | # Check if cmake is available 8 | if ! command -v cmake >/dev/null 2>&1; then 9 | echo "Error: cmake is required but not found. Please install cmake." 10 | exit 1 11 | fi 12 | 13 | # Check if make is available 14 | if ! command -v make >/dev/null 2>&1; then 15 | echo "Error: make is required but not found. Please install make." 16 | exit 1 17 | fi 18 | 19 | # Clean previous build 20 | if [ -e "${PKG_ROOT}/build" ]; then 21 | echo "Cleaning previous build directory..." 22 | rm -rf "${PKG_ROOT}/build" 23 | fi 24 | 25 | # Create build directory 26 | echo "Creating build directory..." 27 | mkdir -p "${PKG_ROOT}/build" 28 | 29 | # Run cmake configuration 30 | echo "Running cmake configuration..." 31 | cd "${PKG_ROOT}/build" 32 | if ! cmake -Wno-dev ..; then 33 | echo "Error: cmake configuration failed" 34 | exit 1 35 | fi 36 | 37 | # Build the library 38 | echo "Building library..." 39 | if ! make; then 40 | echo "Error: make build failed" 41 | exit 1 42 | fi 43 | 44 | cd "${PKG_ROOT}" 45 | 46 | # Determine library name based on platform 47 | unameOut="$(uname -s)" 48 | case "${unameOut}" in 49 | Linux*|Darwin*) libname=movr.so;; 50 | CYGWIN*|MINGW*) libname=movr.dll;; 51 | *) echo "Unknown platform: ${unameOut}"; exit 1;; 52 | esac 53 | echo "Machine platform: ${unameOut}" 54 | echo "Expected library: ${libname}" 55 | 56 | # Check if library was built in src directory 57 | if [ ! -e "${PKG_ROOT}/src/${libname}" ]; then 58 | echo "Error: Shared library ${libname} not found in src directory" 59 | exit 1 60 | fi 61 | 62 | echo "Successfully built ${libname} in src directory" 63 | 64 | # Clean up build directory to avoid GNU extensions in package 65 | if [ -e "${PKG_ROOT}/build" ]; then 66 | echo "Cleaning build directory..." 67 | rm -rf "${PKG_ROOT}/build" 68 | fi 69 | 70 | echo "=== Build completed successfully ===" -------------------------------------------------------------------------------- /man/stcoords.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/coords.R 3 | \name{stcoords} 4 | \alias{stcoords} 5 | \title{Spatiotemporal data formatting} 6 | \usage{ 7 | stcoords(x, y = NULL, t = NULL, unite.xy = FALSE, unite.sep = "_") 8 | } 9 | \arguments{ 10 | \item{x}{A vector, data frame or matrix.} 11 | 12 | \item{y}{A vector.} 13 | 14 | \item{t}{A vector.} 15 | 16 | \item{unite.xy}{A boolean indicates if merging x and y coordinates into 17 | a string to represent a unique location.} 18 | 19 | \item{unite.sep}{A separator to use between x and y coordinates.} 20 | } 21 | \value{ 22 | A list of formatted location sequences: 23 | is_1d: boolean, indicate the spatial coordinates are 1D or 2D. 24 | x: a vector of x coordinate. 25 | y: a vector of y coordinate, NULL if unite.xy is TRUE 26 | t: a vector of timestamps. 27 | } 28 | \description{ 29 | Format spatiotemporal series in a unified manner for both 1D and 2D locations. 30 | If \code{x} is a data frame or matrix, \code{y} and \code{t} are omitted. 31 | } 32 | \details{ 33 | If \code{x} is a data frame (3 columns), this function automatically identify spatial 34 | and temporal values by column names, i.e., (x,y,t) and (lat,lon,time). 35 | Otherwise, the column indexes are employed as [, 1] and [, 2] being the space 36 | coordinates and [, 3] being the timestamps. 37 | 38 | If \code{x} is a data frame (2 columns), similar policies are involved, but 39 | alternatively column names (x, t) and (loc, time) are used. 40 | 41 | If \code{x} is a matrix, column indexes are used merely. 42 | 43 | If \code{x} is a vector, dimensions of space coordinates are determined 44 | by both \code{x} and \code{y}, and the time dimension by \code{t}. 45 | } 46 | \examples{ 47 | ## One data frame with columes x, y, t 48 | x <- data.frame(x=rep(1:10, 2), y=rep_each(1:10, 2), t=1:20) 49 | stcoords(x) 50 | 51 | ## One data frame without demanded colume names 52 | x <- data.frame(rep(1:10, 2), rep_each(1:10, 2), 1:20) 53 | 54 | ## One data frame with two columes loc, time 55 | x <- data.frame(loc=rep(1:10, 2), time=1:20) 56 | 57 | ## With vectors 58 | stcoords(x=rep(1:10, 2), t=1:20) 59 | 60 | ## Combine x and y coordinates 61 | x <- data.frame(rep(1:10, 2), rep_each(1:10, 2), 1:20) 62 | stcoords(x, unite.xy=TRUE) 63 | } 64 | -------------------------------------------------------------------------------- /man/plot_flowmap.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/flowmap.R 3 | \name{plot_flowmap} 4 | \alias{plot_flowmap} 5 | \title{Visualize flowmap.} 6 | \usage{ 7 | plot_flowmap( 8 | from_lat, 9 | from_lon, 10 | to_lat, 11 | to_lon, 12 | dist.log = TRUE, 13 | weight = NULL, 14 | weight.log = TRUE, 15 | gc.breaks = 5, 16 | col.pal = c("white", "blue", "black"), 17 | col.pal.bias = 0.3, 18 | col.pal.grad = 200, 19 | new.device = TRUE, 20 | bg = "black", 21 | ... 22 | ) 23 | } 24 | \arguments{ 25 | \item{from_lat}{The latitude coordinates of departing point for mobile transitions.} 26 | 27 | \item{from_lon}{The longitude coordinates of departing point for mobile transitions.} 28 | 29 | \item{to_lat}{The latitude coordinates of arriving point for mobile transitions.} 30 | 31 | \item{to_lon}{The longitude coordinates of arriving point for mobile transitions.} 32 | 33 | \item{dist.log}{Whether using log-scale distance for line color.} 34 | 35 | \item{weight}{The user-defined weight for line color. Larger weight corresponds to lefter color of col.pal.} 36 | 37 | \item{weight.log}{Whether using log-scale weight for line color.} 38 | 39 | \item{gc.breaks}{The number of intermediate points (excluding two ends) to draw a great circle path.} 40 | 41 | \item{col.pal}{A color vector used by \code{colorRampPalette}; must be a valid argument to \code{col2rgb}. 42 | Refer to \url{https://colorbrewer2.org} to derive more palettes.} 43 | 44 | \item{col.pal.bias}{The bias coefficient used by \code{colorRampPalette}. Higher values give more widely 45 | spaced colors at the high end.} 46 | 47 | \item{col.pal.grad}{The number of color grades to differentiate distance.} 48 | 49 | \item{new.device}{Whether creating a new device for current plot. Set this parameter as FALSE when 50 | trying to plot multiple flowmaps in one figure.} 51 | 52 | \item{bg}{The background color for current plot. It is working when new.device is TRUE.} 53 | 54 | \item{...}{Extra parameters for basic plot() function.} 55 | } 56 | \description{ 57 | Visualize the mobility statistics (flowmap) from data. Each row in the data 58 | will generate a line on the map. 59 | } 60 | \seealso{ 61 | \code{\link{gen.sessions}}, \code{\link{flowmap}}, \code{\link{flowmap2}}, 62 | \code{\link{flow.stat}} 63 | } 64 | -------------------------------------------------------------------------------- /R/heatmap.R: -------------------------------------------------------------------------------- 1 | #' Generate color breaks for heatmap 2 | #' 3 | #' Generate color breaks for heatmap visualization using equal interval classification. 4 | #' 5 | #' @param z A numeric vector of values to classify 6 | #' @param nlevels The number of levels/breaks to generate 7 | #' @return A numeric vector of break points 8 | #' @export 9 | #' @examples 10 | #' heatmap.levels(rnorm(100), nlevels=10) 11 | heatmap.levels <- function(z, nlevels=10) { 12 | # Generate color breaks 13 | brks <- classInt::classIntervals(z, nlevels, style='equal') 14 | brks$brks 15 | } 16 | 17 | #' Heatmap of xyz data 18 | #' 19 | #' Visualize the xyz data in a 2d heatmap. 20 | #' 21 | #' @param x,y the coordinates of each z value. 22 | #' @param z the observed value for each (x,y) point. 23 | #' @param nx,ny the number of bins in x and y dimension, used by \code{\link{vbin.grid}}. 24 | #' @param na replacement for NA value in matrix bins, used by \code{\link{vbin.grid}}. 25 | #' @param nlevels the number of colorful stages to plot, see `filled.contour`. 26 | #' @param levels a numeric vector to indicate the colorful stages, see `filled.contour`. 27 | #' @param colors a vector of color names or hex values, see `colorRampPalette`. Default uses RColorBrewer::brewer.pal(11, "Spectral"). 28 | #' @param bias a positive number. Higher values give more widely spaced colors at the high end, see `colorRampPalette`. 29 | #' @param ... other parameters sent to `filled.contour`. 30 | #' @export 31 | plot.heatmap <- function(x, y, z, nx=100, ny=100, na=0, nlevels=10, levels=NULL, 32 | colors=rev(RColorBrewer::brewer.pal(11, "Spectral")), bias=1, ...) { 33 | # Create color palette 34 | col.pal <- colorRampPalette(colors, bias=bias) 35 | 36 | # Generate 2d heatmap from xyz data 37 | mat <- vbin.grid(x, y, z, na=na, nx=nx, ny=ny) 38 | 39 | # Obtain x and y coordinates for each bin on a 2d surface 40 | x <- as.numeric(rownames(mat)) 41 | y <- as.numeric(colnames(mat)) 42 | 43 | is_empty <- function(x) { is.na(x) | is.null(x) } 44 | 45 | if(is.null(levels)) { 46 | mv <- unlist(mat) 47 | if(is_empty(na)) 48 | mv <- mv[!is_empty(mv)] 49 | levels <- heatmap.levels(mv, nlevels) 50 | } 51 | 52 | # Plot contour-style heatmap 53 | filled.contour(x, y, mat, levels=levels, color.palette=col.pal, ...) 54 | 55 | invisible(list(data=mat, levels=levels)) 56 | } -------------------------------------------------------------------------------- /R/stcorr.R: -------------------------------------------------------------------------------- 1 | #' Calculate pairwise distances in a 2D region 2 | #' 3 | #' Given a random field defined by (x, y) and observation value z, this function 4 | #' calculates the distance between each location pair and appends original observation 5 | #' values into the same row. The output can be exploited to calculate spatial correlations. 6 | #' Note that only Euclidean distance is supported merely for this time. 7 | #' 8 | #' @importFrom dplyr mutate group_by summarise filter select 9 | #' @importFrom magrittr %>% 10 | #' 11 | #' @param x,y the coordinates of given random field 12 | #' @param z the observation value for each point (x,y) 13 | #' @return the data frame of c('r', 'z1', 'z2') 14 | #' @seealso \code{\link{spatial.corr}} 15 | #' @export 16 | pairwise.dist <- function(x, y, z){ 17 | df <- data.frame(x=x, y=y, z=z) 18 | euc.dist <- function(x1, x2) sqrt(sum((x1 - x2) ^ 2)) 19 | 20 | blocks <- nrow(df) 21 | z.mean <- mean(df$z, na.rm=TRUE) 22 | z.var <- var(df$z, na.rm=TRUE) 23 | 24 | do.call(rbind, parallel::mclapply(seq(1, blocks), function(i){ 25 | do.call(rbind, parallel::mclapply(seq(i, blocks), function(j){ 26 | data.frame( 27 | r = euc.dist(df[i, c("x","y")], df[j, c("x","y")]), 28 | z1 = df[i, "z"], 29 | z2 = df[j, "z"]) 30 | })) 31 | })) 32 | } 33 | 34 | #' Calculate spatial correlation of a 2D region 35 | #' 36 | #' @param x,y the coordinates of given random field 37 | #' @param z the observation value for each point (x,y) 38 | #' @param beta the step value to map distances into bins 39 | #' @param corr.method the name of function to calculate correlation, one of c("pearson", "kendall", "spearman") 40 | #' @return r distance between a pair of locations 41 | #' @return n the number of location pairs for specific distance 42 | #' @return corr the correlation value for specific distance 43 | #' @seealso \code{\link{pairwise.dist}} 44 | #' @export 45 | spatial.corr <- function(x, y, z, beta=0.05, corr.method='pearson') { 46 | pairwise.dist(x, y, z) %>% dplyr::mutate( 47 | r.int = findInterval(.data$r, seq(min(.data$r), max(.data$r), by=beta))) %>% 48 | dplyr::group_by(.data$r.int) %>% 49 | dplyr::summarise( 50 | r = mean(.data$r), 51 | n = length(.data$z1), 52 | corr = cor(.data$z1, .data$z2, method=corr.method) 53 | ) %>% dplyr::filter(!is.na(.data$corr)) %>% dplyr::select(-.data$r.int) 54 | } -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(plot,heatmap) 4 | export(RMSE) 5 | export(Rcolors) 6 | export(cal_place_dwelling) 7 | export(cart2geo) 8 | export(cart2geo.radian) 9 | export(deg2rad) 10 | export(dq.iovan) 11 | export(dq.point) 12 | export(dq.point2) 13 | export(dq.traj) 14 | export(dq.traj2) 15 | export(entropy.rand) 16 | export(entropy.space) 17 | export(entropy.spacetime) 18 | export(euc.dist) 19 | export(fit.polyexp) 20 | export(fit.power.law) 21 | export(fit.truncated.power.law) 22 | export(flow.stat) 23 | export(flowmap) 24 | export(flowmap2) 25 | export(gcd) 26 | export(gen.sessions) 27 | export(geo2cart) 28 | export(geo2cart.radian) 29 | export(heatmap.levels) 30 | export(hour2date) 31 | export(hour2tod) 32 | export(hour2tow) 33 | export(in.area) 34 | export(lonlat2xy) 35 | export(map3d) 36 | export(melt_time) 37 | export(midpoint) 38 | export(minor.ticks.axis) 39 | export(pairwise.dist) 40 | export(people.occurrence) 41 | export(plot_flowmap) 42 | export(plot_traj3d) 43 | export(plot_traj_graph) 44 | export(point.coverage) 45 | export(rad2deg) 46 | export(radius.of.gyration) 47 | export(rep_each) 48 | export(rot90) 49 | export(seq_approximate) 50 | export(seq_collapsed) 51 | export(seq_distinct) 52 | export(spatial.corr) 53 | export(standardize) 54 | export(standardize_st) 55 | export(stcoords) 56 | export(traj3d.close) 57 | export(vbin) 58 | export(vbin.grid) 59 | export(vbin.range) 60 | export(voronoi2polygons) 61 | export(voronoi3d) 62 | importFrom(dplyr,.data) 63 | importFrom(dplyr,distinct) 64 | importFrom(dplyr,do) 65 | importFrom(dplyr,filter) 66 | importFrom(dplyr,group_by) 67 | importFrom(dplyr,id) 68 | importFrom(dplyr,left_join) 69 | importFrom(dplyr,mutate) 70 | importFrom(dplyr,select) 71 | importFrom(dplyr,summarise) 72 | importFrom(grDevices,col2rgb) 73 | importFrom(grDevices,colorRampPalette) 74 | importFrom(grDevices,colors) 75 | importFrom(grDevices,rainbow) 76 | importFrom(grDevices,rgb2hsv) 77 | importFrom(graphics,axis) 78 | importFrom(graphics,curve) 79 | importFrom(graphics,filled.contour) 80 | importFrom(graphics,lines) 81 | importFrom(graphics,par) 82 | importFrom(graphics,plot) 83 | importFrom(graphics,rect) 84 | importFrom(graphics,text) 85 | importFrom(graphics,title) 86 | importFrom(igraph,E) 87 | importFrom(igraph,V) 88 | importFrom(igraph,graph.data.frame) 89 | importFrom(igraph,layout.kamada.kawai) 90 | importFrom(igraph,optimal.community) 91 | importFrom(magrittr,"%>%") 92 | importFrom(methods,.hasSlot) 93 | importFrom(stats,cor) 94 | importFrom(stats,median) 95 | importFrom(stats,var) 96 | importFrom(tidyr,separate) 97 | importFrom(tidyr,unite) 98 | useDynLib(movr) 99 | -------------------------------------------------------------------------------- /R/predictability.R: -------------------------------------------------------------------------------- 1 | #' Major concepts, tools and visualization in the predictability paper. 2 | #' Song, C. et al., Limits of predictability in human mobility. 3 | #' Science 2010, 1018. (doi:10.1126/science.1177170) 4 | 5 | #' Mobility entropy 6 | #' 7 | #' In Song's paper, three kinds of entropy are involved to investigate 8 | #' the predictability of human mobility, i.e., random entropy \eqn{S^{rand}}, 9 | #' spatial entropy \eqn{S^{unc}} and spatio-temporal entropy \eqn{S_i}. 10 | #' 11 | #' @param N The number of unique locations. 12 | #' @return The entropy value in bits. 13 | #' @seealso \code{\link{entropy.space}}, \code{\link{entropy.spacetime}} 14 | #' @references 15 | #' Song, C. et al., Limits of predictability in human mobility. 16 | #' Science 2010, 1018. (doi:10.1126/science.1177170) 17 | #' @export 18 | entropy.rand <- function(N) { 19 | log2(N) 20 | } 21 | 22 | #' Mobility entropy 23 | #' 24 | #' In Song's paper, three kinds of entropy are involved to investigate 25 | #' the predictability of human mobility, i.e., random entropy \eqn{S^{rand}}, 26 | #' spatial entropy \eqn{S^{unc}} and spatio-temporal entropy \eqn{S_i}. 27 | #' 28 | #' @param probs A vector of probability to find the person in unique locations. 29 | #' @return The entropy value in bits. 30 | #' @seealso \code{\link{entropy.rand}}, \code{\link{entropy.spacetime}} 31 | #' @references 32 | #' Song, C. et al., Limits of predictability in human mobility. 33 | #' Science 2010, 1018. (doi:10.1126/science.1177170) 34 | #' @export 35 | #' @examples 36 | #' p <- c(0.1, 0.3, 0.5, 0.1) 37 | #' entropy.space(p) 38 | entropy.space <- function(probs) { 39 | -sum(probs * log2(probs)) 40 | } 41 | 42 | #' Mobility entropy 43 | #' 44 | #' In Song's paper, three kinds of entropy are involved to investigate 45 | #' the predictability of human mobility, i.e., random entropy \eqn{S^{rand}}, 46 | #' spatial entropy \eqn{S^{unc}} and spatio-temporal entropy \eqn{S_i}. 47 | #' 48 | #' @param x,y A chronologically ordered sequence of visited locations. The sequence 49 | #' is given by a vector (e.g. location names) or two vectors of location coordinates 50 | #' (e.g., latitude and longitude values). 51 | #' @return The entropy value in bits. 52 | #' @seealso \code{\link{entropy.rand}}, \code{\link{entropy.space}} 53 | #' @references 54 | #' Song, C. et al., Limits of predictability in human mobility. 55 | #' Science 2010, 1018. (doi:10.1126/science.1177170) 56 | #' @export 57 | entropy.spacetime <- function(x, y=NULL) { 58 | stop('Unsupported right now.') 59 | } 60 | 61 | # entropy_estimated <- function(x, y=NULL) { 62 | # 63 | # } 64 | # 65 | # lempel_ziv <- function(S) { 66 | # 67 | # } 68 | # 69 | # predictability <- function() { 70 | # 71 | # } 72 | # 73 | # regularity <- function() { 74 | # 75 | # } -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^packrat/ 4 | ^\.Rprofile$ 5 | .travis.yml 6 | 7 | # Build artifacts and compiled files 8 | ^build/ 9 | ^src/.*\.so$ 10 | ^src/.*\.o$ 11 | ^src/.*\.dylib$ 12 | ^libs/ 13 | ^.*\.tar\.gz$ 14 | ^.*\.Rcheck/ 15 | ^src/.*\.dll$ 16 | ^src/.*\.exe$ 17 | ^src/.*\.a$ 18 | ^src/.*\.lo$ 19 | ^src/.*\.la$ 20 | ^src/.*\.sl$ 21 | ^src/.*\.bundle$ 22 | ^src/.*\.dSYM/ 23 | ^src/.*\.app/ 24 | ^src/.*\.framework/ 25 | ^src/.*\.xcframework/ 26 | ^src/.*\.xcarchive/ 27 | ^src/.*\.ipa$ 28 | ^src/.*\.apk$ 29 | ^src/.*\.deb$ 30 | ^src/.*\.rpm$ 31 | ^src/.*\.msi$ 32 | ^src/.*\.dmg$ 33 | ^src/.*\.pkg$ 34 | ^src/.*\.zip$ 35 | ^src/.*\.tar$ 36 | ^src/.*\.gz$ 37 | ^src/.*\.bz2$ 38 | ^src/.*\.xz$ 39 | ^src/.*\.7z$ 40 | ^src/.*\.rar$ 41 | ^src/.*\.iso$ 42 | ^src/.*\.img$ 43 | ^src/.*\.vmdk$ 44 | ^src/.*\.vhd\.* 45 | ^src/.*\.vhdx\.* 46 | ^src/.*\.qcow2\.* 47 | ^src/.*\.raw\.* 48 | ^src/.*\.bin\.* 49 | ^src/.*\.hex\.* 50 | ^src/.*\.elf\.* 51 | ^src/.*\.out\.* 52 | ^src/.*\.obj\.* 53 | ^src/.*\.class\.* 54 | ^src/.*\.jar\.* 55 | ^src/.*\.war\.* 56 | ^src/.*\.ear\.* 57 | ^src/.*\.pyc\.* 58 | ^src/.*\.pyo\.* 59 | ^src/.*\.pyd\.* 60 | ^src/.*\.so\.* 61 | ^src/.*\.dylib\.* 62 | ^src/.*\.dll\.* 63 | ^src/.*\.a\.* 64 | ^src/.*\.o\.* 65 | ^src/.*\.lo\.* 66 | ^src/.*\.la\.* 67 | ^src/.*\.sl\.* 68 | ^src/.*\.bundle\.* 69 | ^src/.*\.app\.* 70 | ^src/.*\.framework\.* 71 | ^src/.*\.xcframework\.* 72 | ^src/.*\.xcarchive\.* 73 | ^src/.*\.ipa\.* 74 | ^src/.*\.apk\.* 75 | ^src/.*\.deb\.* 76 | ^src/.*\.rpm\.* 77 | ^src/.*\.msi\.* 78 | ^src/.*\.dmg\.* 79 | ^src/.*\.pkg\.* 80 | ^src/.*\.zip\.* 81 | ^src/.*\.tar\.* 82 | ^src/.*\.gz\.* 83 | ^src/.*\.bz2\.* 84 | ^src/.*\.xz\.* 85 | ^src/.*\.7z\.* 86 | ^src/.*\.rar\.* 87 | ^src/.*\.iso\.* 88 | ^src/.*\.img\.* 89 | ^src/.*\.vmdk\.* 90 | ^src/.*\.vhd\.* 91 | ^src/.*\.vhdx\.* 92 | ^src/.*\.qcow2\.* 93 | ^src/.*\.raw\.* 94 | ^src/.*\.bin\.* 95 | ^src/.*\.hex\.* 96 | ^src/.*\.elf\.* 97 | ^src/.*\.out\.* 98 | ^src/.*\.obj\.* 99 | ^src/.*\.class\.* 100 | ^src/.*\.jar\.* 101 | ^src/.*\.war\.* 102 | ^src/.*\.ear\.* 103 | ^src/.*\.pyc\.* 104 | ^src/.*\.pyo\.* 105 | ^src/.*\.pyd\.* 106 | 107 | # CMake build artifacts 108 | ^build/CMakeFiles/ 109 | ^build/CMakeCache\.txt$ 110 | ^build/cmake_install\.cmake$ 111 | ^build/Makefile$ 112 | ^build/.*\.make$ 113 | ^build/CMakeFiles/.*/CMakeCCompilerId\.o$ 114 | ^build/CMakeFiles/.*/CMakeCXXCompilerId\.o$ 115 | ^build/CMakeFiles/.*/.*\.o$ 116 | ^build/CMakeFiles/.*/build\.make$ 117 | 118 | # Hidden files and directories 119 | ^\.git$ 120 | ^\.gitignore$ 121 | ^\.github$ 122 | ^\.vscode$ 123 | ^\.cursor$ 124 | ^\.Rcheck$ 125 | ^\.DS_Store$ 126 | 127 | # Development and build files 128 | ^CODEOWNERS$ 129 | ^docs$ 130 | ^examples$ 131 | ^INSTALL\.md$ 132 | ^Rplots\.pdf$ 133 | ^scripts$ 134 | ^NAMESPACE\.backup$ 135 | 136 | render_docs.R 137 | release.sh 138 | 139 | ^temp_extract/ 140 | 141 | # CMake build system files 142 | ^cmake/ 143 | ^CMakeLists\.txt$ 144 | ^configure$ 145 | ^configure\.win$ 146 | ^.readthedocs.yml$ 147 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(CheckIncludeFiles) 2 | 3 | cmake_minimum_required(VERSION 3.6) 4 | 5 | project(movr) 6 | 7 | # Global options 8 | set(CMAKE_COLOR_MAKEFILE ON) 9 | set(CMAKE_VERBOSE_MAKEFILE OFF) 10 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 11 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") 12 | 13 | # Set generator appropriately for each platform 14 | if(WIN32) 15 | # Use appropriate generator for Windows 16 | if(NOT CMAKE_GENERATOR MATCHES "Visual Studio" AND NOT CMAKE_GENERATOR MATCHES "MinGW Makefiles") 17 | set(CMAKE_GENERATOR "MinGW Makefiles" CACHE INTERNAL "" FORCE) 18 | endif() 19 | else() 20 | # Set generator to Unix Makefiles for Linux/macOS 21 | if(NOT CMAKE_GENERATOR) 22 | set(CMAKE_GENERATOR "Unix Makefiles" CACHE INTERNAL "" FORCE) 23 | endif() 24 | endif() 25 | 26 | # Add portability settings 27 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 28 | set(CMAKE_C_STANDARD 99) 29 | set(CMAKE_C_STANDARD_REQUIRED ON) 30 | 31 | # Platform-specific compiler settings 32 | if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") 33 | add_compile_options(-Wno-pedantic -Wno-unknown-pragmas -Wno-unused-parameter) 34 | elseif(MSVC) 35 | # MSVC-specific settings for Windows 36 | add_compile_options(/W3) 37 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 38 | endif() 39 | 40 | # Windows-specific settings 41 | if(WIN32) 42 | # Ensure proper Windows API definitions 43 | add_definitions(-DWIN32_LEAN_AND_MEAN) 44 | # Handle MinGW vs MSVC differences 45 | if(MINGW) 46 | # MinGW specific settings 47 | set(CMAKE_SHARED_LIBRARY_PREFIX "") 48 | set(CMAKE_SHARED_LIBRARY_SUFFIX ".dll") 49 | endif() 50 | endif() 51 | 52 | # Disable GNU extensions in generated Makefiles for non-Windows platforms 53 | if(NOT WIN32) 54 | set(CMAKE_MAKE_PROGRAM "make" CACHE INTERNAL "" FORCE) 55 | set(CMAKE_GENERATOR_TOOLSET "" CACHE INTERNAL "" FORCE) 56 | set(CMAKE_MAKE_PROGRAM_FLAGS "" CACHE INTERNAL "" FORCE) 57 | endif() 58 | 59 | # Source files 60 | set(SOURCES 61 | src/flowmap.c 62 | src/order.c 63 | src/order.h 64 | ) 65 | 66 | # Find external libraries 67 | find_package(R REQUIRED) 68 | find_package(GLIB REQUIRED) 69 | 70 | # Add libraries and include directories 71 | set(LIBS ${R_LIBRARIES} ${GLIB_LIBRARIES}) 72 | include_directories(${R_INCLUDE_DIR} ${GLIB_INCLUDE_DIRS}) 73 | 74 | # Generate shared library 75 | add_library(${CMAKE_PROJECT_NAME} SHARED ${SOURCES}) 76 | target_link_libraries(${CMAKE_PROJECT_NAME} ${LIBS}) 77 | 78 | # Set output properties for R compatibility - platform specific 79 | if(WIN32) 80 | set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES 81 | PREFIX "" 82 | SUFFIX ".dll" 83 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src" 84 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src" 85 | ) 86 | else() 87 | set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES 88 | PREFIX "" 89 | SUFFIX ".so" 90 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src" 91 | ) 92 | endif() 93 | 94 | # Install target 95 | install(TARGETS ${CMAKE_PROJECT_NAME} DESTINATION lib) -------------------------------------------------------------------------------- /R/fitdists.R: -------------------------------------------------------------------------------- 1 | #' Fit a truncated power-law. 2 | #' 3 | #' Model: y ~ a * x^(-lambda) exp(-x/k) 4 | #' 5 | #' @param x A vector of independent variable. 6 | #' @param y A vector of dependent variable. 7 | #' @param xmin The lower bound point of x. 8 | #' @param xmax The higher truncated point of x. 9 | #' @param plot Whether to plot the fitted curve. 10 | #' @param add Whether to add the fitted curve to current plot. 11 | #' @param ... Extra parameters to \code{\link{curve}}. 12 | #' @return A list of values for a, lambda and k. 13 | #' @seealso \code{\link{fit.power.law}} 14 | #' @export 15 | fit.truncated.power.law <- function(x, y, xmin=min(x), xmax=max(x), plot=TRUE, add=TRUE, ...) { 16 | xtrunc <- (x>=xmin & x<=xmax) 17 | x = x[xtrunc] 18 | y = y[xtrunc] 19 | y[y==0] <- 1e-32 20 | lm.out <- stats::lm(log(y) ~ x + I(log(x))) 21 | p <- stats::coef(lm.out) 22 | lambda = - as.numeric(p[3]) 23 | k = -1 / as.numeric(p[2]) 24 | a = exp(as.numeric(p[1])) 25 | 26 | if (plot) { 27 | curve(exp(-lambda * log(x) - x / k + log(a)), add=add, ...) 28 | } 29 | 30 | invisible(list(model='y ~ a * x^(-lambda) exp(-x/k)', a=a, lambda=lambda, k=k)) 31 | } 32 | 33 | 34 | #' Fit a power-law 35 | #' 36 | #' Model: \eqn{y \sim a \times x^{-\lambda}} 37 | #' 38 | #' @param x A vector of independent variable. 39 | #' @param y A vector of dependent variable. 40 | #' @param xmin The lower bound point of x. 41 | #' @param xmax The higher truncated point of x. 42 | #' @param plot Whether to plot the fitted curve. 43 | #' @param add Whether to add the fitted curve to current plot. 44 | #' @param ... Extra parameters to \code{\link{curve}}. 45 | #' @return A list of values for a and lambda. 46 | #' @seealso \code{\link[igraph]{fit_power_law}}, \code{\link{fit.truncated.power.law}} 47 | #' @export 48 | fit.power.law <- function(x, y, xmin=min(x), xmax=max(x), plot=TRUE, add=TRUE, ...) { 49 | xtrunc <- (x>=xmin & x<=xmax) 50 | x = x[xtrunc] 51 | y = y[xtrunc] 52 | y[y==0] <- 1e-32 53 | lm.out <- stats::lm(log(y) ~ I(log(x))) 54 | p <- stats::coef(lm.out) 55 | a <- exp(as.numeric(p[1])) 56 | lambda <- - as.numeric(p[2]) 57 | 58 | if (plot) { 59 | curve(exp(log(a) - lambda * log(x)), add=add, ...) 60 | } 61 | 62 | invisible(list(model='y ~ a * x^(-lambda)', a=a, lambda=lambda)) 63 | } 64 | 65 | #' Fit a poly-exponential distribution 66 | #' 67 | #' Model: y ~ exp(a*x^2 + b*x + c) * x^d 68 | #' 69 | #' @param x A vector of independent variable. 70 | #' @param y A vector of dependent variable. 71 | #' @param xmin The lower bound point of x. 72 | #' @param xmax The higher truncated point of x. 73 | #' @param plot Whether to plot the fitted curve. 74 | #' @param add Whether to add the fitted curve to current plot. 75 | #' @return A list of values for a, b, c, and d. 76 | #' @param ... Extra parameters to \code{\link{curve}}. 77 | #' @export 78 | fit.polyexp <- function(x, y, xmin=min(x), xmax=max(x), plot=TRUE, add=TRUE, ...){ 79 | xtrunc <- (x>=xmin & x<=xmax) 80 | x = x[xtrunc] 81 | y = y[xtrunc] 82 | y[y==0] <- 1e-32 83 | lm.out <- stats::lm(log(y) ~ x + I(x ^ 2) + I(log(x))) 84 | p <- stats::coef(lm.out) 85 | a=as.numeric(p[1]) 86 | b=as.numeric(p[2]) 87 | c=as.numeric(p[3]) 88 | d=as.numeric(p[4]) 89 | 90 | if (plot) { 91 | curve(exp(a + b * x + c * x^2) * x^d, add=add, ...) 92 | } 93 | 94 | invisible(list(model='y ~ exp(a*x^2 + b*x + c) * x^d', a=a, b=b, c=c, d=d)) 95 | } 96 | -------------------------------------------------------------------------------- /cmake/FindR.cmake: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # - This module locates an installed R distribution. 4 | # 5 | # Defines the following: 6 | # R_COMMAND - Path to R command 7 | # R_HOME - Path to 'R home', as reported by R 8 | # R_INCLUDE_DIR - Path to R include directory 9 | # R_LIBRARY_BASE - Path to R library 10 | # R_LIBRARY_BLAS - Path to Rblas / blas library 11 | # R_LIBRARY_LAPACK - Path to Rlapack / lapack library 12 | # R_LIBRARY_READLINE - Path to readline library 13 | # R_LIBRARIES - Array of: R_LIBRARY_BASE, R_LIBRARY_BLAS, R_LIBRARY_LAPACK, R_LIBRARY_BASE [, R_LIBRARY_READLINE] 14 | # 15 | # VTK_R_HOME - (deprecated, use R_HOME instead) Path to 'R home', as reported by R 16 | # 17 | # Variable search order: 18 | # 1. Attempt to locate and set R_COMMAND 19 | # - If unsuccessful, generate error and prompt user to manually set R_COMMAND 20 | # 2. Use R_COMMAND to set R_HOME 21 | # 3. Locate other libraries in the priority: 22 | # 1. Within a user-built instance of R at R_HOME 23 | # 2. Within an installed instance of R 24 | # 3. Within external system libraries 25 | # 26 | 27 | set(TEMP_CMAKE_FIND_APPBUNDLE ${CMAKE_FIND_APPBUNDLE}) 28 | set(CMAKE_FIND_APPBUNDLE "NEVER") 29 | find_program(R_COMMAND R DOC "R executable.") 30 | set(CMAKE_FIND_APPBUNDLE ${TEMP_CMAKE_FIND_APPBUNDLE}) 31 | 32 | if(R_COMMAND) 33 | execute_process(WORKING_DIRECTORY . 34 | COMMAND ${R_COMMAND} RHOME 35 | OUTPUT_VARIABLE R_ROOT_DIR 36 | OUTPUT_STRIP_TRAILING_WHITESPACE) 37 | # deprecated 38 | if(VTK_R_HOME) 39 | set(R_HOME ${VTK_R_HOME} CACHE PATH "R home directory obtained from R RHOME") 40 | else() 41 | set(R_HOME ${R_ROOT_DIR} CACHE PATH "R home directory obtained from R RHOME") 42 | set(VTK_R_HOME ${R_HOME}) 43 | endif() 44 | # /deprecated 45 | # the following command does nothing currently, but will be used when deprecated code is removed 46 | set(R_HOME ${R_ROOT_DIR} CACHE PATH "R home directory obtained from R RHOME") 47 | 48 | find_path(R_INCLUDE_DIR R.h 49 | HINTS ${R_ROOT_DIR} 50 | PATHS /usr/local/lib /usr/local/lib64 /usr/share 51 | PATH_SUFFIXES include R/include 52 | DOC "Path to file R.h") 53 | 54 | find_library(R_LIBRARY_BASE R 55 | HINTS ${R_ROOT_DIR}/lib 56 | DOC "R library (example libR.a, libR.dylib, etc.).") 57 | 58 | find_library(R_LIBRARY_BLAS NAMES Rblas blas 59 | HINTS ${R_ROOT_DIR}/lib 60 | DOC "Rblas library (example libRblas.a, libRblas.dylib, etc.).") 61 | 62 | find_library(R_LIBRARY_LAPACK NAMES Rlapack lapack 63 | HINTS ${R_ROOT_DIR}/lib 64 | DOC "Rlapack library (example libRlapack.a, libRlapack.dylib, etc.).") 65 | 66 | find_library(R_LIBRARY_READLINE readline 67 | DOC "(Optional) system readline library. Only required if the R libraries were built with readline support.") 68 | 69 | else() 70 | message(SEND_ERROR "FindR.cmake requires the following variables to be set: R_COMMAND") 71 | endif() 72 | 73 | # Note: R_LIBRARY_BASE is added to R_LIBRARIES twice; this may be due to circular linking dependencies; needs further investigation 74 | set(R_LIBRARIES ${R_LIBRARY_BASE} ${R_LIBRARY_BLAS} ${R_LIBRARY_LAPACK} ${R_LIBRARY_BASE}) 75 | if(R_LIBRARY_READLINE) 76 | set(R_LIBRARIES ${R_LIBRARIES} ${R_LIBRARY_READLINE}) 77 | endif() 78 | 79 | # Set R_FOUND if all required components are found 80 | include(FindPackageHandleStandardArgs) 81 | find_package_handle_standard_args(R 82 | REQUIRED_VARS R_HOME R_INCLUDE_DIR R_LIBRARY_BASE 83 | VERSION_VAR R_VERSION 84 | ) 85 | -------------------------------------------------------------------------------- /docs/source/installation.rst: -------------------------------------------------------------------------------- 1 | Installation Guide 2 | ================== 3 | 4 | This guide will help you install the `movr` package on your system. 5 | 6 | System Requirements 7 | ------------------ 8 | 9 | * **R** >= 3.0.0 10 | * **GNU CMake** (for building C extensions) 11 | * **GLib** development libraries 12 | 13 | Operating System Support 14 | ----------------------- 15 | 16 | **`movr` supports Linux, macOS, and Windows systems.** 17 | 18 | * ✅ **Linux**: Ubuntu, Debian, and other Linux distributions 19 | * ✅ **macOS**: All macOS versions (tested on recent releases) 20 | * ✅ **Windows**: Native Windows support (Windows 10/11) 21 | * 🔄 **Windows via WSL**: Also supported through Windows Subsystem for Linux 22 | 23 | Installation Methods 24 | ------------------- 25 | 26 | From GitHub 27 | ~~~~~~~~~~~ 28 | 29 | To install the latest development version from GitHub: 30 | 31 | .. code-block:: r 32 | 33 | # Install devtools if you haven't already 34 | if (!requireNamespace("devtools", quietly = TRUE)) { 35 | install.packages("devtools") 36 | } 37 | 38 | # Install movr from GitHub 39 | devtools::install_github("caesar0301/movr") 40 | 41 | From Source 42 | ~~~~~~~~~~ 43 | 44 | To install from source code: 45 | 46 | .. code-block:: bash 47 | 48 | # Clone the repository 49 | git clone https://github.com/caesar0301/movr.git 50 | cd movr 51 | 52 | # Build C source 53 | ./configure 54 | 55 | # Check package compliance (recommended) 56 | ./scripts/check_cran.sh --quick 57 | 58 | # Or run basic check 59 | R CMD build . 60 | R CMD check movr_*.tar.gz 61 | 62 | # Install 63 | R --no-save -e "library(devtools);install()" 64 | 65 | Platform-Specific Instructions 66 | ----------------------------- 67 | 68 | Ubuntu/Debian 69 | ~~~~~~~~~~~~ 70 | 71 | Install system dependencies: 72 | 73 | .. code-block:: bash 74 | 75 | sudo apt-get update 76 | sudo apt-get install cmake build-essential libglib2.0-dev 77 | 78 | macOS 79 | ~~~~~ 80 | 81 | Install system dependencies using Homebrew: 82 | 83 | .. code-block:: bash 84 | 85 | brew install cmake glib 86 | 87 | Windows 88 | ~~~~~~~ 89 | 90 | Windows builds are now natively supported! The required dependencies are automatically installed during package installation. If you encounter issues, you can manually install: 91 | 92 | * **Rtools**: Download from `CRAN `_ 93 | * **Optional**: For advanced builds, install `MSYS2 `_ for additional Unix-like tools 94 | 95 | Windows (via WSL) 96 | ~~~~~~~~~~~~~~~~~ 97 | 98 | Alternatively, you can use Windows Subsystem for Linux (WSL): 99 | 100 | 1. Install WSL with Ubuntu from Microsoft Store 101 | 2. Follow the Ubuntu/Debian installation instructions above 102 | 103 | Troubleshooting 104 | -------------- 105 | 106 | Common Issues 107 | ~~~~~~~~~~~~ 108 | 109 | **CMake not found** 110 | Make sure CMake is installed and available in your PATH. 111 | 112 | **GLib not found** 113 | Install the GLib development libraries for your system. 114 | 115 | **R not found** 116 | Ensure R is installed and R_HOME is set correctly. 117 | 118 | **Build errors on Windows** 119 | Windows builds are supported natively. Ensure Rtools is installed. If issues persist, try using WSL as an alternative. 120 | 121 | **Permission errors** 122 | Make sure you have write permissions to the R library directory. 123 | 124 | Getting Help 125 | ----------- 126 | 127 | If you encounter installation issues: 128 | 129 | * Check the `GitHub Issues `_ page 130 | * Ensure your system meets the requirements 131 | * Try installing from CRAN first, then from GitHub if needed 132 | * Verify that all system dependencies are installed 133 | 134 | Verification 135 | ----------- 136 | 137 | After installation, verify that `movr` is working correctly: 138 | 139 | .. code-block:: r 140 | 141 | library(movr) 142 | data(movement) 143 | head(movement) 144 | 145 | This should load the package and display the first few rows of the example dataset. -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. movr documentation master file, created by 2 | sphinx-quickstart on Wed Aug 31 15:42:36 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | movr: Human Mobility Analysis in R 7 | ================================== 8 | 9 | **Analyzing and Visualizing Human Mobility Data in R** 10 | 11 | `movr` is an R package that provides comprehensive tools for analyzing and visualizing spatio-temporal human mobility data. It originates from research on human mobility patterns and offers general transformation, calculation, and visualization utilities for mobility analysis. 12 | 13 | .. image:: https://www.r-pkg.org/badges/version/movr 14 | :target: https://cran.r-project.org/package=movr 15 | :alt: CRAN status 16 | 17 | .. image:: https://github.com/caesar0301/movr/workflows/CRAN%20Release%20Check/badge.svg 18 | :target: https://github.com/caesar0301/movr/actions 19 | :alt: R-CMD-check 20 | 21 | .. image:: https://img.shields.io/badge/License-MIT-yellow.svg 22 | :target: https://opensource.org/licenses/MIT 23 | :alt: License: MIT 24 | 25 | .. image:: https://img.shields.io/badge/DOI-10.1016%2Fj.pmcj.2017.02.001-blue.svg 26 | :target: https://doi.org/10.1016/j.pmcj.2017.02.001 27 | :alt: DOI 28 | 29 | Installation 30 | ----------- 31 | 32 | From GitHub: 33 | 34 | .. code-block:: r 35 | 36 | # Install devtools if you haven't already 37 | if (!requireNamespace("devtools", quietly = TRUE)) { 38 | install.packages("devtools") 39 | } 40 | 41 | # Install movr from GitHub 42 | devtools::install_github("caesar0301/movr") 43 | 44 | Quick Start 45 | ---------- 46 | 47 | .. code-block:: r 48 | 49 | # Load the package 50 | library(movr) 51 | 52 | # Load example data 53 | data(movement) 54 | 55 | # Basic trajectory visualization 56 | plot_traj3d(movement, x = "lon", y = "lat", z = "timestamp") 57 | 58 | # Create a flow map 59 | flowmap_data <- flowmap(movement, from = "origin", to = "destination") 60 | plot_flowmap(flowmap_data) 61 | 62 | Features 63 | -------- 64 | 65 | * **3D Trajectory Visualization**: Interactive 3D plots of mobility trajectories 66 | * **Flow Maps**: Visualize population movements and migration patterns 67 | * **Spatial Analysis**: Voronoi tessellation, spatial correlation, and coverage analysis 68 | * **Temporal Analysis**: Time-of-day patterns, session generation, and temporal entropy 69 | * **Statistical Tools**: Radius of gyration, entropy measures, and predictability analysis 70 | * **Data Quality**: Comprehensive data quality assessment and validation tools 71 | 72 | Operating System Support 73 | ----------------------- 74 | 75 | **`movr` supports Linux, macOS, and Windows systems.** 76 | 77 | * ✅ **Linux**: Ubuntu, Debian, and other Linux distributions 78 | * ✅ **macOS**: All macOS versions (tested on recent releases) 79 | * ✅ **Windows**: Native Windows support (Windows 10/11) 80 | * 🔄 **Windows via WSL**: Also supported through Windows Subsystem for Linux 81 | 82 | **Note**: We have tested the package on Ubuntu, macOS, and Windows systems. Windows builds require system dependencies that are automatically handled during installation. 83 | 84 | Contents: 85 | 86 | .. toctree:: 87 | :maxdepth: 2 88 | :caption: Documentation 89 | 90 | installation 91 | quickstart 92 | examples 93 | api 94 | contributing 95 | 96 | Indices and tables 97 | ================== 98 | 99 | * :ref:`genindex` 100 | * :ref:`modindex` 101 | * :ref:`search` 102 | 103 | Citation 104 | -------- 105 | 106 | If you use `movr` in your research, please cite: 107 | 108 | .. code-block:: bibtex 109 | 110 | @article{CHEN2017464, 111 | author = {Xiaming Chen and Haiyang Wang and Siwei Qiang and Yongkun Wang and Yaohui Jin}, 112 | title = {Discovering and modeling meta-structures in human behavior from city-scale cellular data}, 113 | journal = {Pervasive and Mobile Computing}, 114 | volume = {40}, 115 | pages = {464--476}, 116 | year = {2017}, 117 | doi = {https://doi.org/10.1016/j.pmcj.2017.02.001}, 118 | url = {https://www.sciencedirect.com/science/article/pii/S1574119217300743} 119 | } 120 | 121 | -------------------------------------------------------------------------------- /docs/source/quickstart.rst: -------------------------------------------------------------------------------- 1 | Quick Start Guide 2 | ================= 3 | 4 | This guide will help you get started with `movr` quickly. 5 | 6 | Loading the Package 7 | ------------------ 8 | 9 | First, load the `movr` package and example data: 10 | 11 | .. code-block:: r 12 | 13 | library(movr) 14 | data(movement) 15 | 16 | # View the structure of the example data 17 | str(movement) 18 | head(movement) 19 | 20 | Basic Trajectory Visualization 21 | ----------------------------- 22 | 23 | Create a simple 3D trajectory plot: 24 | 25 | .. code-block:: r 26 | 27 | # Basic 3D trajectory visualization 28 | plot_traj3d(movement, 29 | x = "lon", y = "lat", z = "timestamp", 30 | color_by = "user_id", 31 | alpha = 0.7) 32 | 33 | Flow Map Analysis 34 | ---------------- 35 | 36 | Create and visualize flow maps: 37 | 38 | .. code-block:: r 39 | 40 | # Create flow map from mobility data 41 | flow_data <- flowmap(movement, 42 | from = "origin_cell", 43 | to = "destination_cell", 44 | weight = "flow_count") 45 | 46 | # Visualize with custom styling 47 | plot_flowmap(flow_data, 48 | node_size = "population", 49 | edge_width = "flow_strength", 50 | color_scheme = "viridis") 51 | 52 | Spatial Analysis 53 | --------------- 54 | 55 | Calculate radius of gyration and spatial correlations: 56 | 57 | .. code-block:: r 58 | 59 | # Calculate radius of gyration 60 | rog <- radius_of_gyration(movement, 61 | x = "lon", y = "lat", 62 | id = "user_id") 63 | 64 | # Spatial correlation analysis 65 | spatial_corr <- spatial.corr(movement, 66 | x = "lon", y = "lat", 67 | time_window = "daily") 68 | 69 | Temporal Analysis 70 | ---------------- 71 | 72 | Analyze time-of-day patterns and generate sessions: 73 | 74 | .. code-block:: r 75 | 76 | # Time-of-day analysis 77 | tod_data <- hour2tod(movement$timestamp) 78 | 79 | # Generate mobility sessions 80 | sessions <- gen_sessions(movement, 81 | id = "user_id", 82 | time_threshold = 3600) # 1 hour 83 | 84 | # Calculate temporal entropy 85 | temp_entropy <- entropy.spacetime(movement, 86 | id = "user_id", 87 | time_bins = 24) 88 | 89 | Data Quality Assessment 90 | ---------------------- 91 | 92 | Assess the quality of your mobility data: 93 | 94 | .. code-block:: r 95 | 96 | # Comprehensive data quality check 97 | dq_result <- dq.traj(movement, 98 | id = "user_id", 99 | time = "timestamp", 100 | x = "lon", y = "lat") 101 | 102 | # Point-level quality assessment 103 | point_quality <- dq.point(movement, 104 | x = "lon", y = "lat", 105 | time = "timestamp") 106 | 107 | Advanced Visualizations 108 | ---------------------- 109 | 110 | Create more complex visualizations: 111 | 112 | .. code-block:: r 113 | 114 | # Voronoi tessellation in 3D 115 | voronoi_result <- voronoi3d(movement, 116 | x = "lon", y = "lat", z = "timestamp") 117 | 118 | # Interactive 3D map visualizations 119 | map3d_result <- map3d(movement, 120 | x = "lon", y = "lat", z = "timestamp", 121 | terrain = TRUE, 122 | buildings = TRUE) 123 | 124 | Next Steps 125 | ---------- 126 | 127 | Now that you've completed the quick start: 128 | 129 | 1. Explore the `examples` section for more detailed examples 130 | 2. Check the `api` section for complete function documentation 131 | 3. Read the `research` section to understand potential applications 132 | 4. Visit the `GitHub repository `_ for the latest updates 133 | 134 | Getting Help 135 | ----------- 136 | 137 | If you need help: 138 | 139 | * Use `?function_name` for detailed function documentation 140 | * Check the `vignettes` with `vignette(package = "movr")` 141 | * Report issues on `GitHub `_ -------------------------------------------------------------------------------- /scripts/render_docs.R: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env Rscript 2 | # Enhanced Documentation Generation Script for movr package 3 | # This script generates documentation with explicit importFrom statements 4 | 5 | # Set CRAN mirror to avoid issues 6 | options(repos = c(CRAN = "https://cran.rstudio.com/")) 7 | 8 | cat("=== movr Package Documentation Generation ===\n") 9 | 10 | # Load required packages 11 | if (!requireNamespace("devtools", quietly = TRUE)) { 12 | stop("Package 'devtools' is required. Please install it with: install.packages('devtools')") 13 | } 14 | 15 | if (!requireNamespace("roxygen2", quietly = TRUE)) { 16 | stop("Package 'roxygen2' is required. Please install it with: install.packages('roxygen2')") 17 | } 18 | 19 | cat("1. Cleaning previous documentation...\n") 20 | # Clean previous documentation 21 | if (dir.exists("man")) { 22 | unlink("man", recursive = TRUE) 23 | cat(" - Removed existing man/ directory\n") 24 | } 25 | 26 | cat("2. Generating documentation with roxygen2...\n") 27 | # Generate documentation with all roclets 28 | devtools::document( 29 | roclets = c("collate", "namespace", "rd") 30 | ) 31 | 32 | cat("3. Validating NAMESPACE file...\n") 33 | # Check if NAMESPACE was generated 34 | if (!file.exists("NAMESPACE")) { 35 | stop("NAMESPACE file was not generated!") 36 | } 37 | 38 | # Read and display NAMESPACE content 39 | namespace_content <- readLines("NAMESPACE") 40 | cat(" - NAMESPACE file generated successfully\n") 41 | cat(" - Contains", length(namespace_content), "lines\n") 42 | 43 | # Count different types of entries 44 | importFrom_count <- sum(grepl("^importFrom", namespace_content)) 45 | export_count <- sum(grepl("^export", namespace_content)) 46 | s3method_count <- sum(grepl("^S3method", namespace_content)) 47 | useDynLib_count <- sum(grepl("^useDynLib", namespace_content)) 48 | 49 | cat(" - importFrom statements:", importFrom_count, "\n") 50 | cat(" - export statements:", export_count, "\n") 51 | cat(" - S3method statements:", s3method_count, "\n") 52 | cat(" - useDynLib statements:", useDynLib_count, "\n") 53 | 54 | cat("4. Checking for potential issues...\n") 55 | # Check for common issues 56 | if (importFrom_count == 0) { 57 | cat(" - WARNING: No importFrom statements found. Consider adding @importFrom annotations to R files.\n") 58 | } 59 | 60 | if (export_count == 0) { 61 | cat(" - WARNING: No export statements found. Check @export annotations in R files.\n") 62 | } 63 | 64 | # Check for specific packages that should be imported 65 | required_packages <- c("dplyr", "tidyr", "igraph", "magrittr") 66 | for (pkg in required_packages) { 67 | if (!any(grepl(paste0("importFrom\\(", pkg, ","), namespace_content))) { 68 | cat(" - NOTE: No explicit imports from '", pkg, "'. Consider adding @importFrom annotations.\n", sep = "") 69 | } 70 | } 71 | 72 | cat("5. Building package for documentation check...\n") 73 | # Clean any previous builds 74 | if (dir.exists("build")) { 75 | unlink("build", recursive = TRUE) 76 | } 77 | 78 | # Build package 79 | build_result <- devtools::build() 80 | if (!file.exists(build_result)) { 81 | stop("Package build failed") 82 | } 83 | 84 | cat(" - Package built successfully: ", basename(build_result), "\n") 85 | 86 | cat("6. Running R CMD check on built package...\n") 87 | # Run a quick check on the built package 88 | check_result <- tryCatch({ 89 | system(paste("R CMD check", build_result, "--no-manual --no-vignettes --no-tests"), intern = TRUE, ignore.stderr = TRUE) 90 | }, error = function(e) { 91 | NULL 92 | }) 93 | 94 | if (!is.null(check_result) && length(check_result) > 0 && !is.null(attr(check_result, "status")) && attr(check_result, "status") == 0) { 95 | cat(" - Documentation check passed!\n") 96 | } else { 97 | cat(" - WARNING: Documentation check failed or returned non-zero status.\n") 98 | cat(" - This is normal for packages with C extensions.\n") 99 | } 100 | 101 | cat("7. Documentation generation complete!\n") 102 | cat(" - NAMESPACE file: NAMESPACE\n") 103 | cat(" - Documentation files: man/\n") 104 | cat(" - Built package: ", basename(build_result), "\n") 105 | 106 | cat("\n=== Next Steps ===\n") 107 | cat("1. Review the generated NAMESPACE file\n") 108 | cat("2. Add @importFrom annotations to R files for better organization\n") 109 | cat("3. Run './scripts/check_cran.sh' to verify everything works\n") 110 | cat("4. Commit the changes to version control\n") -------------------------------------------------------------------------------- /R/great_circle_distance.R: -------------------------------------------------------------------------------- 1 | # Calculate great circle distance. See 2 | # https://www.r-bloggers.com/2010/11/great-circle-distance-calculations-in-r/ 3 | 4 | #' Great Circle Distance (GCD) 5 | #' 6 | #' Calculates the geodesic distance between two points specified by radian 7 | #' latitude/longitude using one of the Spherical Law of Cosines (slc), 8 | #' the Haversine formula (hf), or the Vincenty inverse formula for 9 | #' ellipsoids (vif). 10 | #' 11 | #' @param p1 Location of point 1 with (lat, long) coordinates. 12 | #' @param p2 Location of point 2 with (lat, long) coordinates. 13 | #' @param type Specific algorithm to use, c('slc', 'hf', 'vif'). 14 | #' @param na Value to return for NA distances 15 | #' @return Distance in kilometers (km). 16 | #' @references \url{ 17 | #' https://www.r-bloggers.com/2010/11/great-circle-distance-calculations-in-r/} 18 | #' @export 19 | #' @examples 20 | #' # Point in (lat, long) format 21 | #' p1 <- c(30.0, 120.0) 22 | #' p2 <- c(30.5, 120.5) 23 | #' 24 | #' gcd(p1, p2) 25 | #' gcd(p1, p2, type="hf") 26 | #' gcd(p1, p2, type="vif") 27 | gcd <- function(p1, p2, type="hf", na=0) { 28 | lat1 = deg2rad(p1[1]) 29 | lon1 = deg2rad(p1[2]) 30 | lat2 = deg2rad(p2[1]) 31 | lon2 = deg2rad(p2[2]) 32 | 33 | if (type == "slc") { 34 | ddd <- gcd.slc(lat1, lon1, lat2, lon2) 35 | } else if (type == "hf") { 36 | ddd <- gcd.hf(lat1, lon1, lat2, lon2) 37 | } else if (type == "vif") { 38 | ddd <- gcd.vif(lat1, lon1, lat2, lon2)[1] 39 | } else { 40 | stop("Unknown type argument: supporting one of 'slc', 'hf', 'vif'.") 41 | } 42 | 43 | if(is.na(ddd)) 44 | ddd <- na 45 | ddd 46 | } 47 | 48 | # Calculates the geodesic distance between two points specified by radian 49 | # latitude/longitude using the Spherical Law of Cosines (slc). 50 | gcd.slc <- function(lat1, long1, lat2, long2) { 51 | # Earth mean radius [km] 52 | R <- 6371 53 | t <- acos(sin(lat1)*sin(lat2) 54 | + cos(lat1)*cos(lat2) * cos(long2-long1)) 55 | t * R 56 | } 57 | 58 | # Calculates the geodesic distance between two points specified by radian 59 | # latitude/longitude using the Haversine formula (hf). 60 | gcd.hf <- function(lat1, long1, lat2, long2) { 61 | # Earth mean radius [km] 62 | R <- 6371 63 | delta.long <- (long2 - long1) 64 | delta.lat <- (lat2 - lat1) 65 | a <- sin(delta.lat/2)^2 + cos(lat1) * cos(lat2) * sin(delta.long/2)^2 66 | c <- 2 * asin(min(1,sqrt(a))) 67 | R * c 68 | } 69 | 70 | # Calculates the geodesic distance between two points specified by radian 71 | # latitude/longitude using Vincenty inverse formula for ellipsoids (vif). 72 | gcd.vif <- function(lat1, long1, lat2, long2) { 73 | # WGS-84 ellipsoid parameters: 74 | # length of major axis of the ellipsoid (radius at equator) 75 | a <- 6378137 76 | # ength of minor axis of the ellipsoid (radius at the poles) 77 | b <- 6356752.314245 78 | # flattening of the ellipsoid 79 | f <- 1/298.257223563 80 | 81 | # difference in longitude 82 | L <- long2-long1 83 | # reduced latitude 84 | U1 <- atan((1-f) * tan(lat1)) 85 | # reduced latitude 86 | U2 <- atan((1-f) * tan(lat2)) 87 | 88 | sinU1 <- sin(U1) 89 | cosU1 <- cos(U1) 90 | sinU2 <- sin(U2) 91 | cosU2 <- cos(U2) 92 | 93 | cosSqAlpha <- NULL 94 | sinSigma <- NULL 95 | cosSigma <- NULL 96 | cos2SigmaM <- NULL 97 | sigma <- NULL 98 | 99 | lambda <- L 100 | lambdaP <- 0 101 | iterLimit <- 100 102 | while (abs(lambda-lambdaP) > 1e-12 & iterLimit>0) { 103 | sinLambda <- sin(lambda) 104 | cosLambda <- cos(lambda) 105 | sinSigma <- sqrt( 106 | (cosU2*sinLambda) * (cosU2*sinLambda) + 107 | (cosU1*sinU2-sinU1*cosU2*cosLambda) * 108 | (cosU1*sinU2-sinU1*cosU2*cosLambda) ) 109 | 110 | if (sinSigma==0) 111 | return(0) # Co-incident points 112 | 113 | cosSigma <- sinU1*sinU2 + cosU1*cosU2*cosLambda 114 | sigma <- atan2(sinSigma, cosSigma) 115 | sinAlpha <- cosU1 * cosU2 * sinLambda / sinSigma 116 | cosSqAlpha <- 1 - sinAlpha*sinAlpha 117 | cos2SigmaM <- cosSigma - 2*sinU1*sinU2/cosSqAlpha 118 | 119 | if (is.na(cos2SigmaM)) 120 | cos2SigmaM <- 0 # Equatorial line: cosSqAlpha=0 121 | 122 | C <- f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha)) 123 | lambdaP <- lambda 124 | lambda <- L + (1-C) * f * sinAlpha * 125 | (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM))) 126 | iterLimit <- iterLimit - 1 127 | } 128 | 129 | if (iterLimit==0) 130 | return(NA) # formula failed to converge 131 | 132 | uSq <- cosSqAlpha * (a*a - b*b) / (b*b) 133 | A <- 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq))) 134 | B <- uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq))) 135 | deltaSigma = B*sinSigma*( 136 | cos2SigmaM+ 137 | B/4*(cosSigma*(-1+2*cos2SigmaM^2)- 138 | B/6*cos2SigmaM*(-3+4*sinSigma^2)*(-3+4*cos2SigmaM^2))) 139 | s <- b*A*(sigma-deltaSigma) / 1000 140 | s 141 | } -------------------------------------------------------------------------------- /scripts/check_cran.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # CRAN Release Check Wrapper Script for movr package 3 | # This script provides an easy interface to run comprehensive CRAN release testing 4 | 5 | set -e # Exit on any error 6 | 7 | # Colors for output 8 | RED='\033[0;31m' 9 | GREEN='\033[0;32m' 10 | YELLOW='\033[1;33m' 11 | BLUE='\033[0;34m' 12 | NC='\033[0m' # No Color 13 | 14 | # Function to print colored output 15 | print_status() { 16 | echo -e "${BLUE}[INFO]${NC} $1" 17 | } 18 | 19 | print_success() { 20 | echo -e "${GREEN}[SUCCESS]${NC} $1" 21 | } 22 | 23 | print_warning() { 24 | echo -e "${YELLOW}[WARNING]${NC} $1" 25 | } 26 | 27 | print_error() { 28 | echo -e "${RED}[ERROR]${NC} $1" 29 | } 30 | 31 | # Function to check if R is available 32 | check_r() { 33 | if ! command -v R &> /dev/null; then 34 | print_error "R is not installed or not in PATH" 35 | exit 1 36 | fi 37 | print_success "R found: $(R --version | head -1)" 38 | } 39 | 40 | # Function to install required R packages 41 | install_required_packages() { 42 | print_status "Checking required R packages..." 43 | 44 | required_packages=("devtools" "roxygen2" "spelling" "rcmdcheck" "rhub") 45 | missing_packages=() 46 | 47 | for pkg in "${required_packages[@]}"; do 48 | if ! R --slave -e "requireNamespace('$pkg', quietly = TRUE)" &> /dev/null; then 49 | missing_packages+=("$pkg") 50 | fi 51 | done 52 | 53 | if [ ${#missing_packages[@]} -gt 0 ]; then 54 | print_warning "Missing required packages: ${missing_packages[*]}" 55 | read -p "Install missing packages? (y/n): " -n 1 -r 56 | echo 57 | if [[ $REPLY =~ ^[Yy]$ ]]; then 58 | print_status "Installing required packages..." 59 | R --slave -e "install.packages(c($(printf "'%s'," "${missing_packages[@]}" | sed 's/,$//')), repos = 'https://cran.rstudio.com/')" 60 | print_success "Required packages installed" 61 | else 62 | print_error "Cannot proceed without required packages" 63 | exit 1 64 | fi 65 | else 66 | print_success "All required packages are installed" 67 | fi 68 | } 69 | 70 | # Function to show usage 71 | show_usage() { 72 | echo "Usage: $0 [OPTIONS]" 73 | echo "" 74 | echo "Options:" 75 | echo " -h, --help Show this help message" 76 | echo " -i, --install Install required packages and exit" 77 | echo " -q, --quick Run quick check (skip some tests)" 78 | echo " -v, --verbose Run with verbose output" 79 | echo "" 80 | echo "Examples:" 81 | echo " $0 # Run full CRAN release check" 82 | echo " $0 --quick # Run quick check" 83 | echo " $0 --install # Install required packages" 84 | echo "" 85 | echo "This script performs comprehensive testing required for CRAN submission." 86 | } 87 | 88 | # Function to run quick check 89 | run_quick_check() { 90 | print_status "Running quick CRAN check..." 91 | Rscript scripts/cran_release_check.R --quick 92 | } 93 | 94 | # Function to run full check 95 | run_full_check() { 96 | print_status "Running full CRAN release check..." 97 | Rscript scripts/cran_release_check.R 98 | } 99 | 100 | # Parse command line arguments 101 | QUICK_MODE=false 102 | VERBOSE=false 103 | INSTALL_ONLY=false 104 | 105 | while [[ $# -gt 0 ]]; do 106 | case $1 in 107 | -h|--help) 108 | show_usage 109 | exit 0 110 | ;; 111 | -i|--install) 112 | INSTALL_ONLY=true 113 | shift 114 | ;; 115 | -q|--quick) 116 | QUICK_MODE=true 117 | shift 118 | ;; 119 | -v|--verbose) 120 | VERBOSE=true 121 | shift 122 | ;; 123 | *) 124 | print_error "Unknown option: $1" 125 | show_usage 126 | exit 1 127 | ;; 128 | esac 129 | done 130 | 131 | # Main execution 132 | main() { 133 | echo "=== movr Package CRAN Release Check ===" 134 | echo "" 135 | 136 | # Check if we're in the right directory 137 | if [ ! -f "DESCRIPTION" ]; then 138 | print_error "DESCRIPTION file not found. Please run this script from the package root directory." 139 | exit 1 140 | fi 141 | 142 | # Check R installation 143 | check_r 144 | 145 | # Install packages if requested 146 | if [ "$INSTALL_ONLY" = true ]; then 147 | install_required_packages 148 | print_success "Package installation completed" 149 | exit 0 150 | fi 151 | 152 | # Install required packages if needed 153 | install_required_packages 154 | 155 | # Run appropriate check 156 | if [ "$QUICK_MODE" = true ]; then 157 | run_quick_check 158 | else 159 | run_full_check 160 | fi 161 | 162 | echo "" 163 | print_success "CRAN release check completed!" 164 | } 165 | 166 | # Run main function 167 | main "$@" -------------------------------------------------------------------------------- /src/flowmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Windows compatibility 7 | #ifdef _WIN32 8 | #include 9 | #ifndef snprintf 10 | #define snprintf _snprintf 11 | #endif 12 | #ifndef vsnprintf 13 | #define vsnprintf _vsnprintf 14 | #endif 15 | #endif 16 | 17 | #include 18 | 19 | #include "order.h" 20 | 21 | /** 22 | * Compress individual's movement history 23 | * 24 | * The compression procedure removes duplicate consecutive records at the same location 25 | * and compresses then into a single session with start and end time. 26 | * 27 | * @param loc character vector 28 | * @param time real vector of timestamps in seconds 29 | * @param gap a length one vector to separate two sessions when the location 30 | * does not change 31 | * @return a data frame of compressed movement data 32 | */ 33 | SEXP 34 | _compress_mov(SEXP loc, SEXP time, SEXP gap) { 35 | // convert R objects to C data structure 36 | size_t NLEN, MLEN, *ordered, idx, i, j, k; 37 | double *time_, gap_, *last_time, *cur_time; 38 | int *loc_; 39 | SEXP out_df, loc_v, stime_v, etime_v; 40 | 41 | NLEN = LENGTH(loc); 42 | loc_ = INTEGER(loc); 43 | time_ = REAL(time); 44 | gap_ = asReal(gap); 45 | 46 | int *last_loc, *cur_loc, *loc_arr[NLEN]; 47 | double *stime_arr[NLEN], *etime_arr[NLEN]; 48 | 49 | // sort observations in time 50 | ordered = malloc(sizeof(size_t) * NLEN); 51 | order(time_, NLEN, sizeof(double), cmpDouble, ordered); 52 | 53 | for ( i = 0, j = 0; i < NLEN; i++) { 54 | idx = ordered[i]; 55 | cur_loc = &loc_[idx]; 56 | cur_time = &time_[idx]; 57 | 58 | if ( i == 0 ){ 59 | loc_arr[j] = cur_loc; 60 | stime_arr[j] = cur_time; 61 | etime_arr[j] = cur_time; 62 | j++; 63 | } else { 64 | if (*cur_loc == *last_loc && *cur_time - *last_time <= gap_) { 65 | // the same session, update last session 66 | etime_arr[j-1] = cur_time; 67 | } else { 68 | // a new session 69 | loc_arr[j] = cur_loc; 70 | stime_arr[j] = cur_time; 71 | etime_arr[j] = cur_time; 72 | j++; 73 | } 74 | } 75 | 76 | last_loc = cur_loc; 77 | last_time = cur_time; 78 | } 79 | 80 | free(ordered); 81 | 82 | // convert C data structure into R objects 83 | MLEN = j; 84 | PROTECT(out_df = NEW_LIST(3)); 85 | PROTECT(loc_v = NEW_INTEGER(MLEN)); 86 | PROTECT(stime_v = NEW_NUMERIC(MLEN)); 87 | PROTECT(etime_v = NEW_NUMERIC(MLEN)); 88 | 89 | for ( k = 0; k < MLEN; k++ ) { 90 | INTEGER(loc_v)[k] = *loc_arr[k]; 91 | REAL(stime_v)[k] = *stime_arr[k]; 92 | REAL(etime_v)[k] = *etime_arr[k]; 93 | } 94 | 95 | SET_VECTOR_ELT(out_df, 0, loc_v); 96 | SET_VECTOR_ELT(out_df, 1, stime_v); 97 | SET_VECTOR_ELT(out_df, 2, etime_v); 98 | 99 | UNPROTECT(4); 100 | 101 | return out_df; 102 | } 103 | 104 | /** 105 | * Calculate the flow statistic for each link (of a location pair) 106 | * 107 | * @param loc character vector 108 | * @param stime real vector 109 | * @param etime real vector 110 | * @param gap length one vector of real number 111 | */ 112 | SEXP 113 | _flow_stat(SEXP loc, SEXP stime, SEXP etime, SEXP gap) { 114 | double *stime_ = REAL(stime); 115 | double *etime_ = REAL(etime); 116 | double gap_ = asReal(gap); 117 | char *last_loc, *cur_loc; 118 | double last_et; 119 | int i; 120 | 121 | GHashTable *stat = g_hash_table_new(g_str_hash, g_int_equal); 122 | last_loc = (char *)CHAR(STRING_ELT(loc, 0)); 123 | last_et = etime_[0]; 124 | 125 | for ( i = 1; i < length(loc); i++ ){ 126 | if ( stime_[i] - last_et <= gap_ ) { 127 | // assemble new link name 128 | char *link; 129 | cur_loc = (char *)CHAR(STRING_ELT(loc, i)); 130 | size_t link_len = strlen(last_loc) + strlen(cur_loc) + 3; 131 | link = malloc(sizeof(char) * link_len); 132 | snprintf(link, link_len, "%s->%s", last_loc, cur_loc); 133 | 134 | // update flow stat 135 | int *new_v, *old_v; 136 | if ( ! g_hash_table_contains(stat, link) ) { 137 | new_v = g_malloc(sizeof(int)); 138 | *new_v = 1; 139 | g_hash_table_insert(stat, link, new_v); 140 | } else { 141 | old_v = (int *)g_hash_table_lookup(stat, link); 142 | *old_v = *old_v + 1; 143 | free(link); // Free link since we don't need to insert it again 144 | } 145 | } 146 | 147 | last_loc = (char *)CHAR(STRING_ELT(loc, i)); 148 | last_et = etime_[i]; 149 | } 150 | 151 | // convert flow stat in hash table to R data frame 152 | SEXP out, edges, flows; 153 | const int PAIR_NUM = g_hash_table_size(stat); 154 | GHashTableIter iter; 155 | gpointer key, value; 156 | i = 0; 157 | 158 | PROTECT(out = NEW_LIST(2)); 159 | PROTECT(edges = NEW_CHARACTER(PAIR_NUM)); 160 | PROTECT(flows = NEW_INTEGER(PAIR_NUM)); 161 | 162 | g_hash_table_iter_init(&iter, stat); 163 | while (g_hash_table_iter_next (&iter, &key, &value)) { 164 | SET_STRING_ELT(edges, i, mkChar((char*)key)); 165 | INTEGER(flows)[i] = *(int *)value; 166 | i++; 167 | } 168 | 169 | SET_VECTOR_ELT(out, 0, edges); 170 | SET_VECTOR_ELT(out, 1, flows); 171 | 172 | UNPROTECT(3); 173 | 174 | // Clean up hash table and all allocated memory 175 | g_hash_table_destroy(stat); 176 | 177 | return out; 178 | } 179 | 180 | // Function registration 181 | static const R_CallMethodDef callMethods[] = { 182 | {"_compress_mov", (DL_FUNC) &_compress_mov, 3}, 183 | {"_flow_stat", (DL_FUNC) &_flow_stat, 4}, 184 | {NULL, NULL, 0} 185 | }; 186 | 187 | void R_init_movr(DllInfo *info) { 188 | R_registerRoutines(info, NULL, callMethods, NULL, NULL); 189 | R_useDynamicSymbols(info, FALSE); 190 | } 191 | -------------------------------------------------------------------------------- /cmake/FindGLIB.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find Glib and its components (gio, gobject etc) 2 | # Once done, this will define 3 | # 4 | # GLIB_FOUND - system has Glib 5 | # GLIB_INCLUDE_DIRS - the Glib include directories 6 | # GLIB_LIBRARIES - link these to use Glib 7 | # 8 | # Optionally, the COMPONENTS keyword can be passed to find_package() 9 | # and Glib components can be looked for. Currently, the following 10 | # components can be used, and they define the following variables if 11 | # found: 12 | # 13 | # gio: GLIB_GIO_LIBRARIES 14 | # gobject: GLIB_GOBJECT_LIBRARIES 15 | # gmodule: GLIB_GMODULE_LIBRARIES 16 | # gthread: GLIB_GTHREAD_LIBRARIES 17 | # 18 | # Note that the respective _INCLUDE_DIR variables are not set, since 19 | # all headers are in the same directory as GLIB_INCLUDE_DIRS. 20 | # 21 | # Copyright (C) 2012 Raphael Kubo da Costa 22 | # 23 | # Redistribution and use in source and binary forms, with or without 24 | # modification, are permitted provided that the following conditions 25 | # are met: 26 | # 1. Redistributions of source code must retain the above copyright 27 | # notice, this list of conditions and the following disclaimer. 28 | # 2. Redistributions in binary form must reproduce the above copyright 29 | # notice, this list of conditions and the following disclaimer in the 30 | # documentation and/or other materials provided with the distribution. 31 | # 32 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS 33 | # IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 34 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 35 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS 36 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 37 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 38 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 39 | # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 40 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 41 | # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 42 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 | 44 | find_package(PkgConfig) 45 | pkg_check_modules(PC_GLIB QUIET glib-2.0) 46 | 47 | find_library(GLIB_LIBRARIES 48 | NAMES glib-2.0 49 | HINTS ${PC_GLIB_LIBDIR} 50 | ${PC_GLIB_LIBRARY_DIRS} 51 | ) 52 | 53 | # Files in glib's main include path may include glibconfig.h, which, 54 | # for some odd reason, is normally in $LIBDIR/glib-2.0/include. 55 | get_filename_component(_GLIB_LIBRARY_DIR ${GLIB_LIBRARIES} PATH) 56 | find_path(GLIBCONFIG_INCLUDE_DIR 57 | NAMES glibconfig.h 58 | HINTS ${PC_LIBDIR} ${PC_LIBRARY_DIRS} ${_GLIB_LIBRARY_DIR} 59 | ${PC_GLIB_INCLUDEDIR} ${PC_GLIB_INCLUDE_DIRS} 60 | PATH_SUFFIXES glib-2.0/include 61 | ) 62 | 63 | find_path(GLIB_INCLUDE_DIR 64 | NAMES glib.h 65 | HINTS ${PC_GLIB_INCLUDEDIR} 66 | ${PC_GLIB_INCLUDE_DIRS} 67 | PATH_SUFFIXES glib-2.0 68 | ) 69 | 70 | set(GLIB_INCLUDE_DIRS ${GLIB_INCLUDE_DIR} ${GLIBCONFIG_INCLUDE_DIR}) 71 | 72 | # Version detection 73 | file(READ "${GLIBCONFIG_INCLUDE_DIR}/glibconfig.h" GLIBCONFIG_H_CONTENTS) 74 | string(REGEX MATCH "#define GLIB_MAJOR_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") 75 | set(GLIB_VERSION_MAJOR "${CMAKE_MATCH_1}") 76 | string(REGEX MATCH "#define GLIB_MINOR_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") 77 | set(GLIB_VERSION_MINOR "${CMAKE_MATCH_1}") 78 | string(REGEX MATCH "#define GLIB_MICRO_VERSION ([0-9]+)" _dummy "${GLIBCONFIG_H_CONTENTS}") 79 | set(GLIB_VERSION_MICRO "${CMAKE_MATCH_1}") 80 | set(GLIB_VERSION "${GLIB_VERSION_MAJOR}.${GLIB_VERSION_MINOR}.${GLIB_VERSION_MICRO}") 81 | 82 | # Additional Glib components. We only look for libraries, as not all of them 83 | # have corresponding headers and all headers are installed alongside the main 84 | # glib ones. 85 | foreach (_component ${GLIB_FIND_COMPONENTS}) 86 | if (${_component} STREQUAL "gio") 87 | find_library(GLIB_GIO_LIBRARIES NAMES gio-2.0 HINTS ${_GLIB_LIBRARY_DIR}) 88 | set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GIO_LIBRARIES) 89 | elseif (${_component} STREQUAL "gobject") 90 | find_library(GLIB_GOBJECT_LIBRARIES NAMES gobject-2.0 HINTS ${_GLIB_LIBRARY_DIR}) 91 | set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GOBJECT_LIBRARIES) 92 | elseif (${_component} STREQUAL "gmodule") 93 | find_library(GLIB_GMODULE_LIBRARIES NAMES gmodule-2.0 HINTS ${_GLIB_LIBRARY_DIR}) 94 | set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GMODULE_LIBRARIES) 95 | elseif (${_component} STREQUAL "gthread") 96 | find_library(GLIB_GTHREAD_LIBRARIES NAMES gthread-2.0 HINTS ${_GLIB_LIBRARY_DIR}) 97 | set(ADDITIONAL_REQUIRED_VARS ${ADDITIONAL_REQUIRED_VARS} GLIB_GTHREAD_LIBRARIES) 98 | elseif (${_component} STREQUAL "gio-unix") 99 | # gio-unix is compiled as part of the gio library, but the include paths 100 | # are separate from the shared glib ones. Since this is currently only used 101 | # by WebKitGTK+ we don't go to extraordinary measures beyond pkg-config. 102 | pkg_check_modules(GIO_UNIX QUIET gio-unix-2.0) 103 | endif () 104 | endforeach () 105 | 106 | include(FindPackageHandleStandardArgs) 107 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLIB REQUIRED_VARS GLIB_INCLUDE_DIRS GLIB_LIBRARIES ${ADDITIONAL_REQUIRED_VARS} 108 | VERSION_VAR GLIB_VERSION) 109 | 110 | mark_as_advanced( 111 | GLIBCONFIG_INCLUDE_DIR 112 | GLIB_GIO_LIBRARIES 113 | GLIB_GIO_UNIX_LIBRARIES 114 | GLIB_GMODULE_LIBRARIES 115 | GLIB_GOBJECT_LIBRARIES 116 | GLIB_GTHREAD_LIBRARIES 117 | GLIB_INCLUDE_DIR 118 | GLIB_INCLUDE_DIRS 119 | GLIB_LIBRARIES 120 | ) 121 | -------------------------------------------------------------------------------- /docs/source/api.rst: -------------------------------------------------------------------------------- 1 | API Reference 2 | ============= 3 | 4 | This section provides links to the complete API documentation for all functions in the `movr` package. 5 | 6 | Function Categories 7 | ------------------ 8 | 9 | Visualization Functions 10 | ~~~~~~~~~~~~~~~~~~~~~~ 11 | 12 | * :func:`plot_traj3d` - 3D trajectory visualization 13 | * :func:`plot_flowmap` - Flow map visualization 14 | * :func:`plot_traj_graph` - Trajectory graph visualization 15 | * :func:`plot_traj3d` - 3D trajectory plots 16 | * :func:`plot.heatmap` - Heatmap visualization 17 | 18 | Flow Analysis 19 | ~~~~~~~~~~~~ 20 | 21 | * :func:`flowmap` - Create flow maps from mobility data 22 | * :func:`flowmap2` - Alternative flow map creation 23 | * :func:`flow.stat` - Flow statistics 24 | 25 | Spatial Analysis 26 | ~~~~~~~~~~~~~~~ 27 | 28 | * :func:`radius_of_gyration` - Calculate radius of gyration 29 | * :func:`spatial.corr` - Spatial correlation analysis 30 | * :func:`point.coverage` - Point coverage analysis 31 | * :func:`people.occurrence` - People occurrence analysis 32 | * :func:`voronoi3d` - 3D Voronoi tessellation 33 | * :func:`voronoi2polygons` - 2D Voronoi tessellation 34 | 35 | Temporal Analysis 36 | ~~~~~~~~~~~~~~~~ 37 | 38 | * :func:`hour2tod` - Time-of-day analysis 39 | * :func:`hour2tow` - Time-of-week analysis 40 | * :func:`hour2date` - Hour to date conversion 41 | * :func:`gen_sessions` - Generate mobility sessions 42 | * :func:`entropy.spacetime` - Spatio-temporal entropy 43 | * :func:`entropy.space` - Spatial entropy 44 | * :func:`entropy.rand` - Random entropy 45 | 46 | Data Quality 47 | ~~~~~~~~~~~ 48 | 49 | * :func:`dq.traj` - Trajectory data quality assessment 50 | * :func:`dq.traj2` - Alternative trajectory quality check 51 | * :func:`dq.point` - Point-level quality assessment 52 | * :func:`dq.point2` - Alternative point quality check 53 | * :func:`dq.iovan` - Iovan distance quality check 54 | 55 | Statistical Analysis 56 | ~~~~~~~~~~~~~~~~~~~ 57 | 58 | * :func:`fit.power.law` - Fit power law distribution 59 | * :func:`fit.truncated.power.law` - Fit truncated power law 60 | * :func:`fit.polyexp` - Fit polyexponential distribution 61 | * :func:`RMSE` - Root Mean Square Error calculation 62 | 63 | Coordinate Transformations 64 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 65 | 66 | * :func:`cart2geo` - Cartesian to geographic coordinates 67 | * :func:`geo2cart` - Geographic to Cartesian coordinates 68 | * :func:`cart2geo.radian` - Cartesian to geographic (radians) 69 | * :func:`geo2cart.radian` - Geographic to Cartesian (radians) 70 | * :func:`deg2rad` - Degrees to radians 71 | * :func:`rad2deg` - Radians to degrees 72 | * :func:`lonlat2xy` - Longitude/latitude to x/y coordinates 73 | * :func:`stcoords` - Spatio-temporal coordinates 74 | 75 | Utility Functions 76 | ~~~~~~~~~~~~~~~~ 77 | 78 | * :func:`gcd` - Great circle distance 79 | * :func:`euc.dist` - Euclidean distance 80 | * :func:`pairwise.dist` - Pairwise distances 81 | * :func:`midpoint` - Calculate midpoint 82 | * :func:`in.area` - Check if points are in area 83 | * :func:`rot90` - Rotate matrix 90 degrees 84 | * :func:`rep_each` - Repeat each element 85 | * :func:`melt_time` - Melt time data 86 | * :func:`cal_place_dwelling` - Calculate place dwelling 87 | * :func:`traj3d.close` - Close 3D trajectory 88 | * :func:`standardize` - Standardize data 89 | * :func:`standardize_st` - Spatio-temporal standardization 90 | 91 | Sequence Analysis 92 | ~~~~~~~~~~~~~~~~ 93 | 94 | * :func:`seq_approximate` - Approximate sequence 95 | * :func:`seq_collapsed` - Collapse sequence 96 | * :func:`seq_distinct` - Distinct sequence 97 | * :func:`seq_dist` - Sequence distance 98 | 99 | Binning Functions 100 | ~~~~~~~~~~~~~~~~ 101 | 102 | * :func:`vbin` - Vector binning 103 | * :func:`vbin.range` - Vector binning with range 104 | * :func:`vbin.grid` - Grid-based binning 105 | * :func:`heatmap.levels` - Heatmap levels 106 | 107 | Plotting Utilities 108 | ~~~~~~~~~~~~~~~~~ 109 | 110 | * :func:`minor.ticks.axis` - Minor tick marks for axes 111 | * :func:`Rcolors` - R color palettes 112 | 113 | Getting Help 114 | ----------- 115 | 116 | To get detailed help for any function: 117 | 118 | .. code-block:: r 119 | 120 | # Get help for a specific function 121 | ?plot_traj3d 122 | ?flowmap 123 | ?radius_of_gyration 124 | 125 | # Search for functions 126 | ??trajectory 127 | ??flow 128 | ??spatial 129 | 130 | # View all functions in the package 131 | ls("package:movr") 132 | 133 | # View package information 134 | packageVersion("movr") 135 | sessionInfo() 136 | 137 | Function Arguments 138 | ----------------- 139 | 140 | Most functions in `movr` follow consistent parameter naming: 141 | 142 | * `x`, `y` - Spatial coordinates (longitude, latitude) 143 | * `z` - Temporal coordinate (timestamp) 144 | * `id` - Individual identifier 145 | * `time` - Time column name 146 | * `from`, `to` - Origin and destination for flow analysis 147 | * `weight` - Weight column for flow analysis 148 | 149 | Data Format 150 | ----------- 151 | 152 | The `movr` package expects mobility data in the following format: 153 | 154 | .. code-block:: r 155 | 156 | # Example data structure 157 | movement <- data.frame( 158 | user_id = c("user1", "user1", "user2", "user2"), 159 | timestamp = c("2023-01-01 10:00:00", "2023-01-01 11:00:00", 160 | "2023-01-01 10:30:00", "2023-01-01 11:30:00"), 161 | lon = c(-74.006, -74.007, -73.985, -73.986), 162 | lat = c(40.712, 40.713, 40.758, 40.759) 163 | ) 164 | 165 | Required columns: 166 | * `user_id` - Unique identifier for each individual 167 | * `timestamp` - Time of the location record 168 | * `lon` - Longitude coordinate 169 | * `lat` - Latitude coordinate 170 | 171 | Optional columns: 172 | * `origin_cell`, `destination_cell` - For flow analysis 173 | * `flow_count`, `population` - For weighted analysis 174 | * Any additional metadata columns 175 | 176 | For more detailed information about each function, use the R help system: 177 | 178 | .. code-block:: r 179 | 180 | help(package = "movr") -------------------------------------------------------------------------------- /.github/workflows/cran-check.yml: -------------------------------------------------------------------------------- 1 | name: CRAN Release Check 2 | 3 | on: 4 | push: 5 | branches: [ main, master, develop ] 6 | pull_request: 7 | branches: [ main, master ] 8 | 9 | env: 10 | R_KEEP_PKG_SOURCE: yes 11 | R_REMOTES_NO_ERRORS_FROM_WARNINGS: true 12 | 13 | jobs: 14 | cran-check: 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | include: 20 | - os: ubuntu-latest 21 | r: 'release' 22 | - os: macos-latest 23 | r: 'release' 24 | - os: windows-latest 25 | r: 'release' 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - uses: r-lib/actions/setup-r@v2 30 | with: 31 | r-version: ${{ matrix.r }} 32 | use-public-rspm: true 33 | 34 | - uses: r-lib/actions/setup-pandoc@v2 35 | 36 | - name: Setup environment and install dependencies (Linux/macOS) 37 | if: runner.os != 'Windows' 38 | run: | 39 | # Setup virtual display (Linux) 40 | if [ "$RUNNER_OS" = "Linux" ]; then 41 | Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 42 | echo "export DISPLAY=:99" >> $GITHUB_ENV 43 | 44 | # Install system dependencies 45 | sudo apt-get update 46 | sudo apt-get install -y cmake build-essential libblas-dev liblapack-dev libglpk40 libglpk-dev libglib2.0-dev libgl1-mesa-dev libglu1-mesa-dev xvfb pkg-config openjdk-11-jdk 47 | echo "JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64" >> $GITHUB_ENV 48 | echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig:$PKG_CONFIG_PATH" >> $GITHUB_ENV 49 | elif [ "$RUNNER_OS" = "macOS" ]; then 50 | # macOS dependencies 51 | brew install cmake glib mesa pkg-config openjdk@11 52 | echo "PKG_CONFIG_PATH=$(brew --prefix glib)/lib/pkgconfig:$PKG_CONFIG_PATH" >> $GITHUB_ENV 53 | if [ -d "/usr/local/opt/openjdk@11/" ]; then 54 | echo "JAVA_HOME=/usr/local/opt/openjdk@11/libexec/openjdk.jdk/Contents/Home" >> $GITHUB_ENV 55 | fi 56 | fi 57 | 58 | # Verify Java installation 59 | java -version 60 | 61 | - name: Setup environment and install dependencies (Windows) 62 | if: runner.os == 'Windows' 63 | run: | 64 | # Windows dependencies using PowerShell 65 | choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' -y 66 | choco install pkgconfiglite -y 67 | choco install openjdk11 -y 68 | 69 | # Install MSYS2 for GLib and other Unix tools 70 | choco install msys2 -y 71 | 72 | # Add MSYS2 to PATH 73 | echo "C:\tools\msys64\usr\bin" >> $env:GITHUB_PATH 74 | echo "C:\tools\msys64\mingw64\bin" >> $env:GITHUB_PATH 75 | 76 | # Install GLib via MSYS2 77 | C:\tools\msys64\usr\bin\bash.exe -lc "pacman -S --noconfirm mingw-w64-x86_64-glib2 mingw-w64-x86_64-pkg-config mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc" 78 | 79 | # Set environment variables 80 | echo "JAVA_HOME=C:\Program Files\Eclipse Adoptium\jdk-11.0.16.101-hotspot" >> $env:GITHUB_ENV 81 | echo "PKG_CONFIG_PATH=C:\tools\msys64\mingw64\lib\pkgconfig" >> $env:GITHUB_ENV 82 | echo "PATH=C:\tools\msys64\mingw64\bin;C:\tools\msys64\usr\bin;$env:PATH" >> $env:GITHUB_ENV 83 | 84 | # Verify installations 85 | java -version 86 | pkg-config --version 87 | pkg-config --modversion glib-2.0 88 | shell: pwsh 89 | 90 | - name: Install R packages (Linux/macOS) 91 | if: runner.os != 'Windows' 92 | run: | 93 | R -e "install.packages(c('remotes', 'rcmdcheck'))" 94 | R -e "remotes::install_deps(dependencies = TRUE)" 95 | R -e "remotes::install_cran(c('devtools', 'roxygen2', 'spelling', 'rhub'))" 96 | 97 | # Install platform-specific packages 98 | if [ "$RUNNER_OS" = "macOS" ]; then 99 | R -e "tryCatch(install.packages('rgl', type = 'source'), error = function(e) install.packages('rgl'))" 100 | R -e "install.packages('OpenStreetMap')" 101 | R -e "library(rgl)" 102 | fi 103 | 104 | - name: Install R packages (Windows) 105 | if: runner.os == 'Windows' 106 | run: | 107 | R.exe -e "options(repos = 'https://cran.rstudio.com/')" 108 | R.exe -e "install.packages(c('remotes', 'rcmdcheck'), type = 'binary')" 109 | R.exe -e "remotes::install_deps(dependencies = TRUE)" 110 | R.exe -e "install.packages(c('devtools', 'roxygen2', 'spelling'), type = 'binary')" 111 | R.exe -e "tryCatch(install.packages('rhub', type = 'binary'), error = function(e) message('rhub install failed, continuing...'))" 112 | R.exe -e "tryCatch(install.packages('rgl', type = 'binary'), error = function(e) message('rgl install failed, continuing...'))" 113 | R.exe -e "tryCatch(install.packages('OpenStreetMap', type = 'binary'), error = function(e) message('OpenStreetMap install failed, continuing...'))" 114 | shell: pwsh 115 | 116 | - name: Build and check package (Linux/macOS) 117 | if: runner.os != 'Windows' 118 | run: | 119 | R -e "devtools::build()" 120 | Rscript scripts/cran_release_check.R 121 | 122 | - name: Build and check package (Windows) 123 | if: runner.os == 'Windows' 124 | continue-on-error: true 125 | run: | 126 | # Windows-specific build (uses configure.win) 127 | R.exe -e "devtools::build()" 128 | # Use simpler check for Windows instead of complex cran_release_check.R 129 | R.exe -e "devtools::check()" 130 | shell: pwsh 131 | 132 | - name: Upload artifacts 133 | uses: actions/upload-artifact@v4 134 | with: 135 | name: cran-check-results-${{ matrix.os }}-${{ matrix.r }} 136 | path: | 137 | *.tar.gz 138 | *.zip 139 | build/ 140 | check.log 141 | final_check.log 142 | -------------------------------------------------------------------------------- /R/rgl_layers.R: -------------------------------------------------------------------------------- 1 | #' Add a 3D map surface 2 | #' 3 | #' This method add a 3D map surface to the RGL plot. The backend map service is 4 | #' supported by OpenStreetMap package. All parameters except for h are 5 | #' consistent with the 'openmap' function in OSM. 6 | #' 7 | #' @note This function requires the \pkg{rgl} package to be installed. 8 | #' You can install it with \code{install.packages("rgl")}. 9 | #' 10 | #' @importFrom dplyr distinct 11 | #' 12 | #' @param lowerLeft the lower left lat and long 13 | #' @param upperRight the upper right lat and long 14 | #' @param h the horizontal plane to locate the map surface 15 | #' @param type the map type to use 16 | #' @param ... all other parameters of \code{\link[OpenStreetMap]{openmap}} 17 | #' @export 18 | #' @seealso \code{\link[OpenStreetMap]{openmap}} 19 | #' @examples 20 | #' data(movement) 21 | #' 22 | #' u1 <- subset(movement, id==3) 23 | #' u1$time <- (u1$time - min(u1$time)) / 3600 24 | 25 | #' lat1 <- min(u1$lat) - 0.005 26 | #' lat2 <- max(u1$lat) + 0.005 27 | #' lon1 <- min(u1$lon) - 0.005 28 | #' lon2 <- max(u1$lon) + 0.005 29 | #' \dontrun{ 30 | #' if(require(OpenStreetMap) && require(rgl)){ 31 | #' library(rgl) 32 | #' rgl.clear() 33 | #' rgl.clear("lights") 34 | #' rgl.bg(color="lightgray") 35 | #' rgl.viewpoint(theta=240, phi=45) 36 | #' rgl.light(theta = 45, phi = 45, viewpoint.rel=TRUE) 37 | #' map3d(c(lat1, lon1), c(lat2, lon2), h=min(u1$time)) 38 | #' axes3d(edges = "bbox", labels = TRUE, tick = TRUE, nticks = 5, box=FALSE, 39 | #' expand = 1.03, col="black", lwd=0.8) 40 | #' invisible(readline(prompt="Press [enter] to continue")) 41 | #' rgl.close() 42 | #' }} 43 | map3d <- function(lowerLeft, upperRight, h=0, type='bing', ...) { 44 | if (!requireNamespace("rgl", quietly = TRUE)) { 45 | stop("Package 'rgl' is required for this function. Please install it with: install.packages('rgl')") 46 | } 47 | if (!requireNamespace("OpenStreetMap", quietly = TRUE)) { 48 | stop("Package 'OpenStreetMap' is required for this function. Please install it with: install.packages('OpenStreetMap')") 49 | } 50 | 51 | upperLeft <- c(upperRight[1], lowerLeft[2]) 52 | lowerRight <- c(lowerLeft[1], upperRight[2]) 53 | map <- OpenStreetMap::openmap(upperLeft, lowerRight, type=type, ...) 54 | map <- OpenStreetMap::openproj(map) 55 | 56 | if(length(map$tiles)!=1){ 57 | stop("multiple tiles not implemented") 58 | } 59 | 60 | tile = map$tiles[[1]] 61 | xres = tile$xres # number of tiles in longitude 62 | yres = tile$yres # number of tiles in latitude 63 | p1 = tile$bbox$p1 # upleft corner 64 | p2 = tile$bbox$p2 # downright corner 65 | 66 | lonmin = min(p1[1], p2[1]) # longitude 67 | lonmax = max(p1[1], p2[1]) 68 | latmin = min(p1[2], p2[2]) # latitude 69 | latmax = max(p1[2], p2[2]) 70 | 71 | slon = seq(lonmin, lonmax, len=yres) 72 | slat = seq(latmin, latmax, len=xres) 73 | 74 | col = matrix(tile$colorData, xres, yres, byrow=T) 75 | h <- matrix(h, yres, xres) 76 | 77 | rgl::rgl.surface(slat, slon, t(h), col=col[xres:1,]) 78 | } 79 | 80 | #' 3D voronoi canvas for RGL 81 | #' 82 | #' @note This function requires the \pkg{rgl} package to be installed. 83 | #' You can install it with \code{install.packages("rgl")}. 84 | #' 85 | #' @importFrom dplyr distinct 86 | #' 87 | #' @param x,y Coordinates of points 88 | #' @param group_by Grouping variable for points 89 | #' @param col Colors for each group 90 | #' @param side Which side to project the voronoi diagram ('x', 'y', or 'z') 91 | #' @param col.seg Color for line segments 92 | #' @param lty Line type 93 | #' @param lwd Line width 94 | #' @export 95 | voronoi3d <- function(x, y, group_by=NULL, col=NULL, side='y', col.seg = "grey", lty=1, lwd=1) { 96 | if (!requireNamespace("rgl", quietly = TRUE)) { 97 | stop("Package 'rgl' is required for this function. Please install it with: install.packages('rgl')") 98 | } 99 | stopifnot(length(x) == length(y), length(x) == length(group_by)) 100 | stopifnot(tolower(side) %in% c('x', 'y', 'z')) 101 | 102 | if (is.null(group_by)) { 103 | group_by = rep(1, length(x)) 104 | if (is.null(col)){ 105 | col = colors()[sample(1:600, 1)] 106 | } else { 107 | stopifnot(length(col) == 1) 108 | } 109 | } else { 110 | stopifnot(length(x) == length(group_by)) 111 | group_by = seq_distinct(group_by) 112 | glen = length(unique(group_by)) 113 | if (is.null(col)) { 114 | col = colors()[sample(1:600, glen, replace = FALSE)] 115 | } else { 116 | stopifnot(length(col) == glen) 117 | } 118 | } 119 | 120 | bbox = rgl::par3d('bbox') 121 | br = range(bbox) 122 | if (br[1] - br[2] > 3e+30) 123 | stop("A valid rgl canvas is needed first when calling voronoi3d{movr}.") 124 | xr = bbox[1:2] 125 | yr = bbox[3:4] 126 | zr = bbox[5:6] 127 | side = tolower(side) 128 | coord = cbind(xr, yr, zr)[,which(side == c('x', 'y', 'z'))] 129 | 130 | plot.direchlet.tess <- function(points) { 131 | points.all = dplyr::distinct(points) 132 | dd = deldir::deldir(points.all[,1], points.all[,2]) 133 | dirsgs = dd$dirsgs 134 | p1 = as.vector(t(dirsgs[,c(1,3)])) 135 | p2 = as.vector(t(dirsgs[,c(2,4)])) 136 | p3 = rep(coord[1], 2) 137 | if (side == 'x') { 138 | x = p3 139 | y = p1 140 | z = p2 141 | } else if (side == 'y') { 142 | x = p1 143 | y = p3 144 | z = p2 145 | } else { 146 | x = p1 147 | y = p2 148 | z = p3 149 | } 150 | rgl::rgl.lines(x=x, y=y, z=z, lwd=lwd, lty=lty, color=col.seg) 151 | } 152 | 153 | plot.points <- function(points, col) { 154 | points = dplyr::distinct(points) 155 | s1 = as.vector(points[,1]) 156 | s2 = as.vector(points[,2]) 157 | s3 = rep(coord[1], 2) 158 | if (side == 'x') { 159 | px = s3 160 | py = s1 161 | pz = s2 162 | } else if (side == 'y') { 163 | px = s1 164 | py = s3 165 | pz = s2 166 | } else { 167 | px = s1 168 | py = s2 169 | pz = s3 170 | } 171 | rgl::rgl.points(x=px, y=py, z=pz, color=col, alpha=0.5) 172 | } 173 | 174 | points <- data.frame(x=x, y=y, group=group_by) 175 | plot.direchlet.tess(points) 176 | for ( g in unique(points$group) ) { 177 | plot.points(subset(points, points$group==g), col[g]) 178 | } 179 | } -------------------------------------------------------------------------------- /R/coords.R: -------------------------------------------------------------------------------- 1 | #' Convert degrees to radians. 2 | #' @param deg A number or vector of degrees. 3 | #' 4 | #' @importFrom tidyr unite 5 | #' @importFrom magrittr %>% 6 | #' @seealso \code{\link{rad2deg}} 7 | #' @export 8 | deg2rad <- function(deg) { deg * pi / 180 } 9 | 10 | 11 | #' Convert radians to degrees. 12 | #' @param rad A number or vector of radians 13 | #' @seealso \code{\link{deg2rad}} 14 | #' @export 15 | rad2deg <- function(rad) { rad * 180 / pi } 16 | 17 | 18 | #' Geopoint and Cartesian conversion 19 | #' 20 | #' Converting geo-points in lat/long into Cartesian coordinates. 21 | #' 22 | #' @param x A 2D vector (lat, long) representing the geo-point in degrees. 23 | #' @return A unit-length 3D vector (x, y, z) in Cartesian system. 24 | #' @seealso \code{\link{geo2cart.radian}}, \code{\link{cart2geo}} 25 | #' @export 26 | #' @examples 27 | #' geo2cart(c(30, 120)) 28 | geo2cart <- function(x) { 29 | if ( x[1] < -90 || x[1] > 90 || x[2] < -180 || x[2] > 180) { 30 | stop("Invalid lat/long coordinates.") 31 | } 32 | lat <- deg2rad(x[1]) 33 | lon <- deg2rad(x[2]) 34 | geo2cart.radian(c(lat, lon)) 35 | } 36 | 37 | 38 | #' Convert geopoints in radians to Cartesian coordinates. 39 | #' @param x A 2D vector (lat, long) representing the geo-point in radians. 40 | #' @seealso \code{\link{cart2geo.radian}} 41 | #' @export 42 | geo2cart.radian <- function(x) { 43 | p.x <- cos(x[1]) * cos(x[2]) 44 | p.y <- cos(x[1]) * sin(x[2]) 45 | p.z <- sin(x[1]) 46 | c(p.x, p.y, p.z) 47 | } 48 | 49 | 50 | #' Cartesian and geopoint conversion 51 | #' 52 | #' Converting Cartesian coordinates into long/lat geo-points. 53 | #' 54 | #' @param x A unit-length 3D vector (x, y, z) in Cartesian system. 55 | #' @return A 2D vector (lat, long) representing the geo-point in degree. 56 | #' @seealso \code{\link{geo2cart}}, \code{\link{cart2geo.radian}} 57 | #' @export 58 | #' @examples 59 | #' cart2geo(c(-0.4330127, 0.7500000, 0.5000000)) 60 | cart2geo <- function(x) { 61 | p <- cart2geo.radian(x) 62 | c(rad2deg(p[1]), rad2deg(p[2])) 63 | } 64 | 65 | 66 | #' Convert Cartesian coordinates to geopoints in radians. 67 | #' @param x A 2D vector (lat, long) representing the geo-point in radians. 68 | #' @seealso \code{\link{geo2cart.radian}} 69 | #' @export 70 | cart2geo.radian <- function(x) { 71 | lon = atan2(x[2], x[1]) 72 | hyp = sqrt(x[1]^2 + x[2]^2) 73 | lat = atan2(x[3], hyp) 74 | c(lat, lon) 75 | } 76 | 77 | 78 | #' Spatiotemporal data formatting 79 | #' 80 | #' Format spatiotemporal series in a unified manner for both 1D and 2D locations. 81 | #' If \code{x} is a data frame or matrix, \code{y} and \code{t} are omitted. 82 | #' 83 | #' If \code{x} is a data frame (3 columns), this function automatically identify spatial 84 | #' and temporal values by column names, i.e., (x,y,t) and (lat,lon,time). 85 | #' Otherwise, the column indexes are employed as [, 1] and [, 2] being the space 86 | #' coordinates and [, 3] being the timestamps. 87 | #' 88 | #' If \code{x} is a data frame (2 columns), similar policies are involved, but 89 | #' alternatively column names (x, t) and (loc, time) are used. 90 | #' 91 | #' If \code{x} is a matrix, column indexes are used merely. 92 | #' 93 | #' If \code{x} is a vector, dimensions of space coordinates are determined 94 | #' by both \code{x} and \code{y}, and the time dimension by \code{t}. 95 | #' 96 | #' @param x A vector, data frame or matrix. 97 | #' @param y A vector. 98 | #' @param t A vector. 99 | #' @param unite.xy A boolean indicates if merging x and y coordinates into 100 | #' a string to represent a unique location. 101 | #' @param unite.sep A separator to use between x and y coordinates. 102 | #' @return A list of formatted location sequences: 103 | #' is_1d: boolean, indicate the spatial coordinates are 1D or 2D. 104 | #' x: a vector of x coordinate. 105 | #' y: a vector of y coordinate, NULL if unite.xy is TRUE 106 | #' t: a vector of timestamps. 107 | #' @export 108 | #' @examples 109 | #' ## One data frame with columes x, y, t 110 | #' x <- data.frame(x=rep(1:10, 2), y=rep_each(1:10, 2), t=1:20) 111 | #' stcoords(x) 112 | #' 113 | #' ## One data frame without demanded colume names 114 | #' x <- data.frame(rep(1:10, 2), rep_each(1:10, 2), 1:20) 115 | #' 116 | #' ## One data frame with two columes loc, time 117 | #' x <- data.frame(loc=rep(1:10, 2), time=1:20) 118 | #' 119 | #' ## With vectors 120 | #' stcoords(x=rep(1:10, 2), t=1:20) 121 | #' 122 | #' ## Combine x and y coordinates 123 | #' x <- data.frame(rep(1:10, 2), rep_each(1:10, 2), 1:20) 124 | #' stcoords(x, unite.xy=TRUE) 125 | stcoords <- function(x, y=NULL, t=NULL, unite.xy=FALSE, unite.sep = "_") { 126 | coords <- list() 127 | coords[['is_1d']] = FALSE 128 | 129 | if ( is.matrix(x) ){ 130 | x = as.data.frame(x) 131 | } 132 | 133 | if ( is.data.frame(x) ) { 134 | if (ncol(x) == 2) { # 1D location index 135 | coords[['is_1d']] = TRUE 136 | if ( all(c('x', 't') %in% colnames(x)) ){ 137 | coords[['x']] = x[, 'x'] 138 | coords[['t']] = x[, 't'] 139 | } else if (all(c('loc', 'time') %in% colnames(x))) { 140 | coords[['x']] = x[, 'loc'] 141 | coords[['t']] = x[, 'time'] 142 | } else { 143 | coords[['x']] = x[, 1] 144 | coords[['t']] = x[, 2] 145 | } 146 | } else if ( ncol(x) == 3 ) { # 2D space coordincates 147 | if ( all(c('x', 'y', 't') %in% colnames(x)) ) { 148 | coords[['x']] = x[, 'x'] 149 | coords[['y']] = x[, 'y'] 150 | coords[['t']] = x[, 't'] 151 | } else if (all(c('lon', 'lat', 'time') %in% colnames(x))) { 152 | coords[['x']] = x[, 'lon'] 153 | coords[['y']] = x[, 'lat'] 154 | coords[['t']] = x[, 'time'] 155 | } else { 156 | coords[['x']] = x[, 1] 157 | coords[['y']] = x[, 2] 158 | coords[['t']] = x[, 3] 159 | } 160 | } 161 | } else if (is.vector(x)) { 162 | stopifnot( !is.null(t) || length(t)!=length(x)) 163 | coords[['x']] = x 164 | coords[['t']] = t 165 | coords[['is_1d']] = TRUE 166 | 167 | if ( !is.null(y) ) { 168 | stopifnot( length(y) == length(x) ) 169 | coords[['y']] = y 170 | coords[['is_1d']] = FALSE 171 | } 172 | } else { 173 | stop("Invalid spatiotemporal data to format.") 174 | } 175 | 176 | if (!coords$is_1d && unite.xy) { 177 | df <- data.frame(coords$x, coords$y) %>% 178 | tidyr::unite("loc", c(1, 2), sep=unite.sep) 179 | coords$x <- df$loc 180 | coords$y <- NULL 181 | coords$is_1d <- TRUE 182 | } 183 | 184 | # return formated coordinates 185 | coords 186 | } 187 | -------------------------------------------------------------------------------- /configure.win: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PKG_ROOT="$(cd "$(dirname "$0")"; pwd)" 3 | 4 | echo "=== movr Package Windows Build Configuration ===" 5 | echo "Package root: ${PKG_ROOT}" 6 | 7 | # Check if cmake is available 8 | if ! command -v cmake >/dev/null 2>&1; then 9 | echo "Error: cmake is required but not found. Please install cmake." 10 | echo "You can download cmake from: https://cmake.org/download/" 11 | exit 1 12 | fi 13 | 14 | # Detect build environment 15 | if command -v gcc >/dev/null 2>&1; then 16 | echo "Found GCC compiler (MinGW)" 17 | BUILD_ENV="MinGW" 18 | elif command -v cl >/dev/null 2>&1; then 19 | echo "Found MSVC compiler" 20 | BUILD_ENV="MSVC" 21 | else 22 | echo "Warning: No suitable compiler found. Trying default..." 23 | BUILD_ENV="Default" 24 | fi 25 | 26 | # Clean previous build 27 | if [ -e "${PKG_ROOT}/build" ]; then 28 | echo "Cleaning previous build directory..." 29 | rm -rf "${PKG_ROOT}/build" 30 | fi 31 | 32 | # Create build directory 33 | echo "Creating build directory..." 34 | mkdir -p "${PKG_ROOT}/build" 35 | 36 | # Run cmake configuration 37 | echo "Running cmake configuration for Windows..." 38 | cd "${PKG_ROOT}/build" 39 | 40 | # Configure based on available build environment 41 | if [ "$BUILD_ENV" = "MinGW" ]; then 42 | echo "Configuring for MinGW..." 43 | if ! cmake -G "MinGW Makefiles" -Wno-dev ..; then 44 | echo "Warning: cmake configuration failed for MinGW, trying fallback method..." 45 | # Fallback: Skip CMake and use R package build system directly 46 | cd "${PKG_ROOT}" 47 | echo "Using R package build system directly instead of CMake..." 48 | # Create a simple Makevars.win that doesn't require CMake 49 | cat > src/Makevars.win.tmp << 'EOF' 50 | # Simple Windows build without CMake 51 | PKG_CPPFLAGS = -I. -DWIN32_LEAN_AND_MEAN -std=c99 52 | PKG_LIBS = -lws2_32 53 | 54 | # Try to use GLib via pkg-config if available 55 | PKG_CONFIG_EXISTS = $(shell pkg-config --exists glib-2.0 && echo yes) 56 | ifeq ($(PKG_CONFIG_EXISTS), yes) 57 | PKG_CPPFLAGS += $(shell pkg-config --cflags glib-2.0) 58 | PKG_LIBS += $(shell pkg-config --libs glib-2.0) 59 | endif 60 | 61 | all: $(SHLIB) 62 | clean: 63 | $(RM) *.o *.dll 64 | EOF 65 | mv src/Makevars.win.tmp src/Makevars.win 66 | echo "Fallback configuration complete" 67 | exit 0 68 | fi 69 | MAKE_CMD="mingw32-make" 70 | elif [ "$BUILD_ENV" = "MSVC" ]; then 71 | echo "Configuring for MSVC..." 72 | if ! cmake -Wno-dev ..; then 73 | echo "Warning: cmake configuration failed for MSVC, trying fallback method..." 74 | cd "${PKG_ROOT}" 75 | echo "Using R package build system directly instead of CMake..." 76 | cat > src/Makevars.win.tmp << 'EOF' 77 | # Simple Windows build without CMake 78 | PKG_CPPFLAGS = -I. -DWIN32_LEAN_AND_MEAN 79 | PKG_LIBS = -lws2_32 80 | 81 | # Try to use GLib via pkg-config if available 82 | PKG_CONFIG_EXISTS = $(shell pkg-config --exists glib-2.0 && echo yes) 83 | ifeq ($(PKG_CONFIG_EXISTS), yes) 84 | PKG_CPPFLAGS += $(shell pkg-config --cflags glib-2.0) 85 | PKG_LIBS += $(shell pkg-config --libs glib-2.0) 86 | endif 87 | 88 | all: $(SHLIB) 89 | clean: 90 | $(RM) *.o *.dll 91 | EOF 92 | mv src/Makevars.win.tmp src/Makevars.win 93 | echo "Fallback configuration complete" 94 | exit 0 95 | fi 96 | MAKE_CMD="cmake --build . --config Release" 97 | else 98 | echo "Trying default cmake configuration..." 99 | if ! cmake -Wno-dev ..; then 100 | echo "Warning: cmake configuration failed, trying fallback method..." 101 | cd "${PKG_ROOT}" 102 | echo "Using R package build system directly instead of CMake..." 103 | cat > src/Makevars.win.tmp << 'EOF' 104 | # Simple Windows build without CMake 105 | PKG_CPPFLAGS = -I. -DWIN32_LEAN_AND_MEAN -std=c99 106 | PKG_LIBS = -lws2_32 107 | 108 | # Try to use GLib via pkg-config if available 109 | PKG_CONFIG_EXISTS = $(shell pkg-config --exists glib-2.0 && echo yes) 110 | ifeq ($(PKG_CONFIG_EXISTS), yes) 111 | PKG_CPPFLAGS += $(shell pkg-config --cflags glib-2.0) 112 | PKG_LIBS += $(shell pkg-config --libs glib-2.0) 113 | endif 114 | 115 | all: $(SHLIB) 116 | clean: 117 | $(RM) *.o *.dll 118 | EOF 119 | mv src/Makevars.win.tmp src/Makevars.win 120 | echo "Fallback configuration complete" 121 | exit 0 122 | fi 123 | MAKE_CMD="make" 124 | fi 125 | 126 | # Build the library 127 | echo "Building library with: ${MAKE_CMD}" 128 | if [ "$BUILD_ENV" = "MSVC" ]; then 129 | if ! cmake --build . --config Release; then 130 | echo "Error: MSVC build failed" 131 | exit 1 132 | fi 133 | else 134 | if ! ${MAKE_CMD}; then 135 | echo "Error: build failed" 136 | exit 1 137 | fi 138 | fi 139 | 140 | cd "${PKG_ROOT}" 141 | 142 | # Determine library name and check if built 143 | unameOut="$(uname -s 2>/dev/null || echo "Windows")" 144 | case "${unameOut}" in 145 | Linux*|Darwin*) libname=movr.so;; 146 | CYGWIN*|MINGW*|MSYS*|Windows*) libname=movr.dll;; 147 | *) libname=movr.dll;; # Default to dll for unknown Windows variants 148 | esac 149 | echo "Platform: ${unameOut}" 150 | echo "Expected library: ${libname}" 151 | 152 | # Check if library was built in src directory 153 | if [ ! -e "${PKG_ROOT}/src/${libname}" ]; then 154 | echo "Error: Shared library ${libname} not found in src directory" 155 | echo "Build may have failed or library may be in a different location" 156 | 157 | # Try to find the library in build directory 158 | if [ -e "${PKG_ROOT}/build/${libname}" ]; then 159 | echo "Found library in build directory, copying to src..." 160 | cp "${PKG_ROOT}/build/${libname}" "${PKG_ROOT}/src/" 161 | elif [ -e "${PKG_ROOT}/build/Release/${libname}" ]; then 162 | echo "Found library in build/Release directory, copying to src..." 163 | cp "${PKG_ROOT}/build/Release/${libname}" "${PKG_ROOT}/src/" 164 | elif [ -e "${PKG_ROOT}/build/Debug/${libname}" ]; then 165 | echo "Found library in build/Debug directory, copying to src..." 166 | cp "${PKG_ROOT}/build/Debug/${libname}" "${PKG_ROOT}/src/" 167 | else 168 | echo "Could not locate built library" 169 | exit 1 170 | fi 171 | fi 172 | 173 | echo "Successfully built ${libname} in src directory" 174 | 175 | # Clean up build directory to avoid issues with R package build 176 | if [ -e "${PKG_ROOT}/build" ]; then 177 | echo "Cleaning build directory..." 178 | rm -rf "${PKG_ROOT}/build" 179 | fi 180 | 181 | echo "=== Windows build completed successfully ===" -------------------------------------------------------------------------------- /R/plot_mobility.R: -------------------------------------------------------------------------------- 1 | #' Visualize trajectories using RGL 3D. 2 | #' 3 | #' @note This function requires the \pkg{rgl} package to be installed. 4 | #' You can install it with \code{install.packages("rgl")}. 5 | #' 6 | #' @importFrom dplyr mutate group_by summarise 7 | #' @importFrom igraph graph.data.frame V E optimal.community layout.kamada.kawai 8 | #' @importFrom magrittr %>% 9 | #' 10 | #' @param x,y Numeric vectors of spatial coordinates 11 | #' @param t The temporal vector for each (x,y) point. 12 | #' @param group_by A group indicator when multiple users are visualized. 13 | #' @param col A vector of color strings. It must have the same length as unique(group_by). 14 | #' @param xlab,ylab,tlab The labels for each axis. 15 | #' @param ... Other parameters for \code{\link[rgl]{plot3d}} or \code{\link[rgl]{axes3d}} 16 | #' @export 17 | #' @examples 18 | #' data(movement) 19 | #' 20 | #' users <- subset(movement, id %in% c(23, 20)) 21 | #' users <- dplyr::mutate(users, time = time/86400 - min(time/86400)) 22 | #' users <- dplyr::filter(users, time <= 30) 23 | #' \dontrun{ 24 | #' if(require(OpenStreetMap) && require(rgl)){ 25 | #' plot_traj3d(users$lon, users$lat, users$time, 26 | #' group_by=users$id, col=c('royalblue', 'orangered')) 27 | #' invisible(readline(prompt="Press [enter] to continue")) 28 | #' traj3d.close() 29 | #' }} 30 | plot_traj3d <- function(x, y, t, group_by=NULL, col=NULL, xlab="", ylab="", tlab="", ...) { 31 | if (!requireNamespace("rgl", quietly = TRUE)) { 32 | stop("Package 'rgl' is required for this function. Please install it with: install.packages('rgl')") 33 | } 34 | 35 | stopifnot(length(x) == length(y) && length(x) == length(t)) 36 | 37 | #t <- strftime(as.POSIXct(t, origin="1970-01-01"), format="%m%d-%H:%M") 38 | 39 | rgl::par3d(windowRect=c(20,40,800,800), cex='0.8') 40 | rgl::rgl.clear() 41 | rgl::rgl.clear("lights") 42 | rgl::rgl.bg(color="white") 43 | rgl::rgl.viewpoint(theta = 40, phi = 10) 44 | rgl::rgl.light(theta = -15, phi = 30, viewpoint.rel=TRUE) 45 | 46 | if (is.null(group_by)) { 47 | group_by = rep(1, length(x)) 48 | if (is.null(col)){ 49 | col = colors()[sample(1:600, 1)] 50 | } else { 51 | stopifnot(length(col) == 1) 52 | } 53 | } else { 54 | stopifnot(length(x) == length(group_by)) 55 | group_by = seq_distinct(group_by) 56 | glen = length(unique(group_by)) 57 | if (is.null(col)) { 58 | col = colors()[sample(1:600, glen, replace = FALSE)] 59 | } else { 60 | stopifnot(length(col) == glen) 61 | } 62 | } 63 | 64 | rgl::plot3d(x, t, y, type='n', xlab=xlab, ylab=tlab, zlab=ylab, axes=FALSE, ...) 65 | 66 | for (g in unique(group_by)) { 67 | x0 = x[group_by==g] 68 | t0 = t[group_by==g] 69 | y0 = y[group_by==g] 70 | rgl::plot3d(x0, t0, y0, type='p', col=col[g], add=TRUE, ...) 71 | rgl::lines3d(x0, t0, y0, color=col[g], ...) 72 | } 73 | 74 | voronoi3d(x, y, group_by, col) 75 | 76 | # rgl::axes3d(edges=c("x--", "y--", "z")) 77 | # rgl::axes3d(lwd=0.7, xlen=8, ylen=10, zlen=8, col='black', marklen=40) 78 | 79 | rgl::axes3d(edges=c('z+-', 'x-+', 'y-+'), 80 | col='black', nticks=7, expand=1, 81 | labels = FALSE, tick = FALSE, ...) 82 | } 83 | 84 | #' Close RGL 3D device 85 | #' 86 | #' Close the current RGL 3D device. This is a convenience function 87 | #' that checks if the rgl package is available before closing. 88 | #' 89 | #' @note This function requires the \pkg{rgl} package to be installed. 90 | #' You can install it with \code{install.packages("rgl")}. 91 | #' 92 | #' @export 93 | #' @examples 94 | #' \dontrun{ 95 | #' # After creating a 3D plot with plot_traj3d 96 | #' traj3d.close() 97 | #' } 98 | traj3d.close <- function() { 99 | if (!requireNamespace("rgl", quietly = TRUE)) { 100 | stop("Package 'rgl' is required for this function. Please install it with: install.packages('rgl')") 101 | } 102 | rgl::rgl.close() 103 | } 104 | 105 | 106 | #' Visualize individual's trajectories. 107 | #' 108 | #' This function plots a trajectory of a specific user as a weighted 109 | #' graph. Each node represents a stay point whose size indicates the 110 | #' (log) length of dwelling time. Each directed edge means the existence 111 | #' transition between stay points and its width indicates the (log) 112 | #' transition frequencies. 113 | #' 114 | #' @param loc A vector of location identifiers 115 | #' @param time A vector of timestamps corresponding to each location 116 | #' @param ... Additional arguments passed to plot function 117 | #' @export 118 | #' @examples 119 | #' data(movement) 120 | #' 121 | #' user <- subset(movement, id==20) 122 | #' \donttest{ 123 | #' plot_traj_graph(user$loc, user$time) 124 | #' } 125 | plot_traj_graph <- function(loc, time, ...) { 126 | user <- data.frame(loc=loc, time=time) 127 | stays <- cal_place_dwelling(user$loc, user$time) 128 | cut.off <- sqrt(median(stays$dwelling)) 129 | stays.cut <- stays[stays$dwelling > cut.off, ] 130 | user.cut <- subset(user, user$loc %in% stays.cut$loc) 131 | movs.cut <- flowmap(1, user.cut$loc, user.cut$time) 132 | 133 | g <- igraph::graph.data.frame(movs.cut, vertices=stays.cut, directed = TRUE) 134 | igraph::V(g)$size <- log(igraph::V(g)$dwelling + 1) * 3 135 | igraph::V(g)$label <- NA 136 | igraph::V(g)$frame.color <- NA 137 | pal <- RColorBrewer::brewer.pal(12, name='Set3') 138 | igraph::V(g)$community <- igraph::optimal.community(g)$membership 139 | igraph::V(g)$color <- pal[vbin(igraph::V(g)$community, 10)] 140 | igraph::E(g)$arrow.size <- .2 141 | igraph::E(g)$curved <- .1 142 | igraph::E(g)$width <- log(igraph::E(g)$total+1) * 2 143 | plot(g, layout=igraph::layout.kamada.kawai, ...) 144 | } 145 | 146 | # Merged by the same locations 147 | merge_dwelling_session <- function(loc, time) { 148 | df <- data.frame(loc, time) 149 | df %>% 150 | dplyr::mutate(grp=cumsum(c(TRUE, diff(.data$loc) != 0))) %>% 151 | dplyr::group_by(.data$grp, .data$loc) %>% 152 | dplyr::summarise(stime=min(.data$time), etime=max(.data$time)) 153 | } 154 | 155 | #' Calculate dwelling period by averaging dwelling 156 | #' time between different consecutive locations 157 | #' 158 | #' @param loc A vector of location identifiers 159 | #' @param time A vector of timestamps corresponding to each location 160 | #' @return A data frame with columns 'loc' and 'dwelling' containing the total dwelling time for each location 161 | #' @export 162 | cal_place_dwelling <- function(loc, time) { 163 | xt <- merge_dwelling_session(loc, time) 164 | xt$dwelling <- xt$etime - xt$stime 165 | L <- nrow(xt) 166 | delta <- 0.5 * (xt$stime[2:L] - xt$etime[1:L-1]) 167 | xt$dwelling <- xt$dwelling + c(delta, 0) + c(0, delta) 168 | df <- data.frame(loc=xt$loc, dwelling=xt$dwelling) 169 | df %>% dplyr::group_by(.data$loc) %>% dplyr::summarise(dwelling=sum(.data$dwelling)) 170 | } -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Release Script for movr package 3 | # This script performs comprehensive testing and builds the package for manual CRAN upload 4 | 5 | set -e # Exit on any error 6 | 7 | # Colors for output 8 | RED='\033[0;31m' 9 | GREEN='\033[0;32m' 10 | YELLOW='\033[1;33m' 11 | BLUE='\033[0;34m' 12 | NC='\033[0m' # No Color 13 | 14 | # Function to print colored output 15 | print_status() { 16 | echo -e "${BLUE}[INFO]${NC} $1" 17 | } 18 | 19 | print_success() { 20 | echo -e "${GREEN}[SUCCESS]${NC} $1" 21 | } 22 | 23 | print_warning() { 24 | echo -e "${YELLOW}[WARNING]${NC} $1" 25 | } 26 | 27 | print_error() { 28 | echo -e "${RED}[ERROR]${NC} $1" 29 | } 30 | 31 | # Function to show usage 32 | show_usage() { 33 | echo "Usage: $0 [OPTIONS]" 34 | echo "" 35 | echo "Options:" 36 | echo " -h, --help Show this help message" 37 | echo "" 38 | echo "Examples:" 39 | echo " $0 # Prepare package for manual CRAN upload" 40 | echo "" 41 | echo "This script performs comprehensive testing and builds the package for manual CRAN upload." 42 | } 43 | 44 | # Function to check environment and dependencies 45 | check_environment() { 46 | print_status "Checking environment and dependencies..." 47 | 48 | # Check if we're in the right directory 49 | if [ ! -f "DESCRIPTION" ]; then 50 | print_error "DESCRIPTION file not found. Please run this script from the package root directory." 51 | exit 1 52 | fi 53 | 54 | # Check required tools 55 | for tool in cmake make R; do 56 | if ! command -v $tool >/dev/null 2>&1; then 57 | print_error "$tool not found" 58 | exit 1 59 | fi 60 | done 61 | 62 | # Check R dependencies 63 | R --slave -e " 64 | required_packages <- c('devtools', 'roxygen2') 65 | missing_packages <- required_packages[!sapply(required_packages, requireNamespace, quietly = TRUE)] 66 | if (length(missing_packages) > 0) { 67 | cat('Missing packages:', paste(missing_packages, collapse = ', '), '\n') 68 | quit(status = 1) 69 | } 70 | " 71 | 72 | if [ $? -ne 0 ]; then 73 | print_error "Missing required R packages. Please install them first." 74 | exit 1 75 | fi 76 | 77 | print_success "Environment check completed" 78 | } 79 | 80 | # Function to build and test package 81 | build_and_test() { 82 | print_status "Building and testing package..." 83 | 84 | # Run configure script 85 | if [ -f "configure" ]; then 86 | ./configure 87 | else 88 | print_error "configure script not found" 89 | exit 1 90 | fi 91 | 92 | # Check build results 93 | if [ ! -f "src/movr.so" ]; then 94 | print_error "movr.so not found in src directory" 95 | exit 1 96 | fi 97 | 98 | # Regenerate documents 99 | Rscript scripts/render_docs.R 100 | 101 | # Run CRAN release check 102 | if [ -f "scripts/check_cran.sh" ]; then 103 | ./scripts/check_cran.sh 104 | if [ $? -ne 0 ]; then 105 | print_error "CRAN release check failed! Please fix the issues before proceeding." 106 | exit 1 107 | fi 108 | else 109 | print_error "CRAN check script not found." 110 | exit 1 111 | fi 112 | 113 | # Build package for release 114 | R --slave -e " 115 | if (requireNamespace('devtools', quietly = TRUE)) { 116 | build_result <- devtools::build() 117 | cat('Package built successfully:', basename(build_result), '\n') 118 | } else { 119 | quit(status = 1) 120 | } 121 | " 122 | 123 | if [ $? -ne 0 ]; then 124 | print_error "Package build failed" 125 | exit 1 126 | fi 127 | 128 | print_success "Build and test completed" 129 | } 130 | 131 | # Function to prepare for manual CRAN upload 132 | prepare_manual_cran_upload() { 133 | print_status "Preparing package for manual CRAN upload..." 134 | 135 | # Run spell check 136 | R --slave -e "options(repos = c(CRAN = 'https://cran.rstudio.com/')); library(devtools); spell_check()" 137 | if [ $? -ne 0 ]; then 138 | print_error "Spell check failed" 139 | exit 1 140 | fi 141 | 142 | # Get package file 143 | BUILD_FILE=$(ls movr_*.tar.gz | head -1) 144 | if [ ! -f "$BUILD_FILE" ]; then 145 | print_error "Package file not found!" 146 | exit 1 147 | fi 148 | 149 | print_success "Package file ready: $BUILD_FILE" 150 | print_status "Package size: $(du -h "$BUILD_FILE" | cut -f1)" 151 | 152 | echo "" 153 | print_status "=== Manual CRAN Upload Instructions ===" 154 | echo "1. Go to: https://cran.r-project.org/submit.html" 155 | echo "2. Fill in the submission form:" 156 | echo " - Package name: movr" 157 | echo " - Version: $(grep '^Version:' DESCRIPTION | cut -d: -f2 | tr -d ' ')" 158 | echo " - Upload the package file: $BUILD_FILE" 159 | echo " - Add any comments about changes" 160 | echo "" 161 | echo "3. Alternative submission methods:" 162 | echo " - Email to: cran@r-project.org" 163 | echo " - Subject: movr package submission" 164 | echo " - Attach: $BUILD_FILE" 165 | echo "" 166 | echo "4. After submission:" 167 | echo " - Monitor: https://cran.r-project.org/web/checks/check_results_movr.html" 168 | echo " - Check email for CRAN feedback" 169 | echo "" 170 | 171 | print_success "Package is ready for manual CRAN upload!" 172 | print_status "Package file location: $(pwd)/$BUILD_FILE" 173 | 174 | # Copy package to desktop if requested 175 | read -p "Copy package to desktop for easy access? (y/n): " -n 1 -r 176 | echo 177 | if [[ $REPLY =~ ^[Yy]$ ]]; then 178 | cp "$BUILD_FILE" ~/Desktop/ 179 | print_success "Package copied to desktop: ~/Desktop/$BUILD_FILE" 180 | fi 181 | } 182 | 183 | # Main release preparation function 184 | run_release_preparation() { 185 | echo "=== movr Package Release Preparation ===" 186 | echo "Date: $(date)" 187 | echo "Platform: $(uname -s) $(uname -m)" 188 | echo "Working directory: $(pwd)" 189 | echo "" 190 | 191 | check_environment 192 | build_and_test 193 | prepare_manual_cran_upload 194 | 195 | echo "" 196 | print_success "Release preparation completed! Package is ready for manual CRAN upload." 197 | } 198 | 199 | # Parse command line arguments 200 | while [[ $# -gt 0 ]]; do 201 | case $1 in 202 | -h|--help) 203 | show_usage 204 | exit 0 205 | ;; 206 | *) 207 | print_error "Unknown option: $1" 208 | show_usage 209 | exit 1 210 | ;; 211 | esac 212 | done 213 | 214 | # Run release preparation 215 | run_release_preparation -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Scripts Directory 2 | 3 | This directory contains all helper scripts for the movr package development, testing, and release process. 4 | 5 | ## 📁 Scripts Overview 6 | 7 | ### Core Scripts 8 | 9 | > **Note**: The `test_build.sh` script has been merged into `release.sh` as the build testing functionality. The unified script now includes all build testing as a preceding step before release operations. 10 | 11 | #### `cran_release_check.R` 12 | **Purpose**: Comprehensive R-based testing engine for CRAN release validation 13 | - **12 test categories** covering all CRAN requirements 14 | - **Detailed validation** with timing and reporting 15 | - **CRAN policy compliance** checks 16 | - **Automated issue detection** and reporting 17 | 18 | **Usage**: 19 | ```bash 20 | Rscript scripts/cran_release_check.R 21 | ``` 22 | 23 | #### `check_cran.sh` 24 | **Purpose**: User-friendly wrapper for comprehensive CRAN release testing 25 | - **Automatic dependency installation** for required R packages 26 | - **Colored output** for better readability 27 | - **Multiple check modes** (full/quick) 28 | - **Interactive error handling** and help system 29 | 30 | **Usage**: 31 | ```bash 32 | ./scripts/check_cran.sh 33 | ./scripts/check_cran.sh --quick 34 | ./scripts/check_cran.sh --install 35 | ``` 36 | 37 | #### `release.sh` 38 | **Purpose**: Unified release script with dry-run and real release modes 39 | - **Build testing** (from test_build.sh) as preceding step 40 | - **Dry-run mode** for testing build and CRAN validation (default) 41 | - **Real release mode** for actual CRAN submission 42 | - **Quick mode** for faster testing 43 | - **Uses devtools** for package building instead of R CMD 44 | 45 | **Usage**: 46 | ```bash 47 | ./scripts/release.sh # Dry-run (test build and validate) 48 | ./scripts/release.sh --dry-run # Same as above 49 | ./scripts/release.sh --release # Perform actual CRAN release 50 | ./scripts/release.sh --quick # Quick dry-run 51 | ./scripts/release.sh --help # Show all options 52 | ``` 53 | 54 | #### `render_docs.R` 55 | **Purpose**: Automated documentation generation with validation 56 | - **Enhanced NAMESPACE generation** with explicit imports 57 | - **Automatic backup** and cleanup 58 | - **Validation and quality assurance** 59 | - **Error handling** for C extensions 60 | 61 | **Usage**: 62 | ```bash 63 | Rscript scripts/render_docs.R 64 | ``` 65 | 66 | ## 🚀 Quick Access from Root Directory 67 | 68 | For convenience, wrapper scripts are available in the root directory: 69 | 70 | - `./release` - Run the complete release process 71 | - `./check-cran` - Run CRAN release checks 72 | - `./render-docs` - Generate documentation 73 | 74 | ## 📋 Script Dependencies 75 | 76 | ### Required R Packages 77 | The scripts automatically manage these dependencies: 78 | - `devtools` - Package development tools 79 | - `roxygen2` - Documentation generation 80 | - `spelling` - Spell checking 81 | - `rcmdcheck` - R CMD check wrapper 82 | 83 | ### System Requirements 84 | - **R** (version 4.0.0 or higher) 85 | - **Bash** shell environment 86 | - **CMake** (for native code building) 87 | 88 | ## 🔧 Script Configuration 89 | 90 | ### Environment Variables 91 | - `R_LIBS_USER` - Custom R library path (optional) 92 | - `R_ENVIRON` - R environment configuration (optional) 93 | 94 | ### Command Line Options 95 | Each script supports various command line options. Use `--help` to see available options: 96 | ```bash 97 | ./scripts/check_cran.sh --help 98 | ./scripts/release.sh --help 99 | ``` 100 | 101 | ## 📊 Test Categories 102 | 103 | The `cran_release_check.R` script performs 12 comprehensive test categories: 104 | 105 | 1. **Package Structure Validation** - Essential files, DESCRIPTION format 106 | 2. **Documentation Generation** - Roxygen2, NAMESPACE, Rd files 107 | 3. **R CMD check (CRAN Standard)** - Full CRAN validation 108 | 4. **Spell Check** - Documentation spelling validation 109 | 5. **Good Practice Check** - Coding standards and best practices 110 | 6. **Package Installation Test** - Build and install verification 111 | 7. **Examples Test** - Documentation examples execution 112 | 8. **Tests Test** - Unit test execution 113 | 9. **CRAN Policy Compliance** - Non-ASCII, license, maintainer 114 | 10. **Dependencies Check** - Package dependency analysis 115 | 11. **Build Artifacts Check** - Unwanted file detection 116 | 12. **Final Package Build** - Clean build and optimization 117 | 118 | ## 🎯 Usage Workflows 119 | 120 | ### Development Workflow 121 | ```bash 122 | # 1. Make changes to R code 123 | # 2. Regenerate documentation 124 | ./render-docs 125 | 126 | # 3. Run quick check 127 | ./check-cran --quick 128 | 129 | # 4. Fix any issues found 130 | # 5. Repeat until clean 131 | ``` 132 | 133 | ### Pre-Release Workflow 134 | ```bash 135 | # 1. Run full CRAN check 136 | ./check-cran 137 | 138 | # 2. Review warnings and notes 139 | # 3. Fix critical issues 140 | # 4. Update NEWS.md 141 | # 5. Increment version number 142 | ``` 143 | 144 | ### Release Workflow 145 | ```bash 146 | # 1. Test build and validate (dry-run) 147 | ./scripts/release.sh --dry-run 148 | 149 | # 2. Review any issues and fix them 150 | # 3. Run quick validation 151 | ./scripts/release.sh --quick 152 | 153 | # 4. Perform actual release 154 | ./scripts/release.sh --release 155 | 156 | # 5. Monitor CRAN submission status 157 | ``` 158 | 159 | ## 🔍 Troubleshooting 160 | 161 | ### Common Issues 162 | 163 | #### Script Not Found 164 | ```bash 165 | # Ensure scripts are executable 166 | chmod +x scripts/*.sh scripts/*.R 167 | 168 | # Check script paths 169 | ls -la scripts/ 170 | ``` 171 | 172 | #### Permission Denied 173 | ```bash 174 | # Fix permissions 175 | chmod +x scripts/*.sh 176 | chmod +x release check-cran render-docs 177 | ``` 178 | 179 | #### R Package Dependencies 180 | ```bash 181 | # Install required packages 182 | ./check-cran --install 183 | ``` 184 | 185 | ### Error Resolution 186 | 1. **Check script permissions** - Ensure scripts are executable 187 | 2. **Verify R installation** - Ensure R is in PATH 188 | 3. **Install dependencies** - Use `--install` option 189 | 4. **Check working directory** - Run from package root 190 | 5. **Review error messages** - Check script output for details 191 | 192 | ## 📚 Related Documentation 193 | 194 | - `../CRAN_RELEASE_GUIDE.md` - Comprehensive CRAN release guide 195 | - `../ENHANCED_BUILD_SYSTEM_SUMMARY.md` - System overview 196 | - `../DOCUMENTATION.md` - Documentation generation guide 197 | 198 | ## 🤝 Contributing 199 | 200 | When adding new scripts: 201 | 1. **Place in scripts directory** - Keep root directory clean 202 | 2. **Update this README** - Document new scripts 203 | 3. **Add wrapper script** - If needed for root access 204 | 4. **Test thoroughly** - Ensure scripts work correctly 205 | 5. **Update documentation** - Keep guides current 206 | 207 | --- 208 | 209 | **🎉 These scripts provide a comprehensive, automated testing and release system for the movr package!** -------------------------------------------------------------------------------- /R/seq.R: -------------------------------------------------------------------------------- 1 | #' Vector Normalization 2 | #' 3 | #' Normalize a given vector. 4 | #' 5 | #' @importFrom dplyr group_by mutate select summarise 6 | #' @importFrom magrittr %>% 7 | #' 8 | #' @param x A vector to be normalized. 9 | #' @seealso \code{\link{standardize_st}} 10 | #' @export 11 | #' @examples 12 | #' standardize(c(1,2,3,4,5,6)) 13 | standardize <- function(x) { 14 | if(length(x) == 1){ 15 | warning('standardize_v takes lenght-1 vector') 16 | return(1) 17 | } 18 | x.min = min(x); 19 | x.max = max(x) 20 | (x - x.min) / (x.max - x.min) 21 | } 22 | 23 | #' Normalization over spatial and temporal scale 24 | #' 25 | #' Scale the value along spatial and temporal coordinates simultaneously. 26 | #' 27 | #' @param scoord a 1D vector of spatial coordinate 28 | #' @param tcoord a 1D vector of temporal coordinate 29 | #' @param value a value vector for each (scoord, tcoord) 30 | #' @param alpha a tuning parameter controlling the weight of space and time 31 | #' @export 32 | #' @examples 33 | #' scoord <- rep(seq(6), 2) 34 | #' tcoord <- rep(c(1,2), each=6) 35 | #' value <- runif(6 * 2) 36 | #' standardize_st(scoord, tcoord, log10(1+value)) 37 | standardize_st <- function(scoord, tcoord, value, alpha=0.5){ 38 | df <- data.frame(s=scoord, t=tcoord, v=value) 39 | df2 <- df %>% 40 | # scaled over time 41 | dplyr::group_by(.data$s) %>% 42 | dplyr::mutate( 43 | z.t = standardize(.data$v) ) %>% 44 | # scaled over space 45 | dplyr::group_by(.data$t) %>% 46 | dplyr::mutate( 47 | z.s = standardize(.data$v), 48 | z = alpha * .data$z.s + (1-alpha) * .data$z.t ) %>% 49 | dplyr::select(-.data$z.t, -.data$z.s) 50 | df2$z 51 | } 52 | 53 | #' Approximately matching sequence 54 | #' 55 | #' Match x to y approximately, and return the index of y, 56 | #' which is mostly near to each value in x. 57 | #' A variate of match() or %in% 58 | #' 59 | #' @param x A given vector to be matched 60 | #' @param y A target vector to calculate absolute approximation 61 | #' @seealso \code{\link{seq_along}}, \code{\link{rep_each}} 62 | #' @export 63 | #' @examples 64 | #' a <- c(1,2,3) 65 | #' b <- c(0.1, 0.2, 0.5) 66 | #' seq_approximate(a, b) 67 | seq_approximate <- function(x, y){ 68 | sapply(x, function(i) which.min(abs(y-i))) 69 | } 70 | 71 | #' Sequencing by distinct values 72 | #' 73 | #' Generate a new (integer) sequence according to distinct value levels. 74 | #' The same value takes a unique order number. 75 | #' 76 | #' @param v A vector to generate integer sequence 77 | #' @export 78 | #' @seealso \code{\link{seq_along}}, \code{\link{seq_collapsed}}, 79 | #' \code{\link{vbin}}, \code{\link{vbin.range}}, \code{\link{vbin.grid}} 80 | #' @examples 81 | #' seq_along(c(1,2,3,2)) 82 | #' seq_distinct(c(1,2,3,2)) 83 | seq_distinct <- function(v) { 84 | v.u <- unique(v) 85 | sapply(v, match, v.u) 86 | } 87 | 88 | #' Sequencing by collapsing adjacent same values 89 | #' 90 | #' Generate integer sequence by assigning the same adjacent values to the same 91 | #' level. 92 | #' 93 | #' @param v The input vector. 94 | #' @seealso \code{\link{seq_along}}, \code{\link{seq_distinct}}, 95 | #' \code{\link{vbin}}, \code{\link{vbin.range}}, \code{\link{vbin.grid}} 96 | #' @export 97 | #' @examples 98 | #' seq_collapsed(c(1,2,2,3,2,2)) 99 | seq_collapsed <- function(v) { 100 | len = length(v) 101 | stopifnot(len > 0) 102 | last_index = 1 103 | res = c(last_index) 104 | if ( len >= 2) { 105 | last_value = v[1] 106 | for (p in v[2:len]) { 107 | if ( last_value != p) { 108 | last_index = last_index + 1 109 | } 110 | res = c(res, last_index) 111 | last_value = p 112 | } 113 | } 114 | res 115 | } 116 | 117 | #' Replicate elements of vector 118 | #' 119 | #' This is a slight modification of \code{rep} in basic package. It replicates 120 | #' each element of a vector one by one to construct a new vector. 121 | #' 122 | #' @param x a vector 123 | #' @param times the number of replication times of each element. 124 | #' @seealso \code{\link{seq_approximate}}, 125 | #' @export 126 | #' @examples 127 | #' rep(1:10, 2) 128 | #' rep_each(1:10, 2) 129 | rep_each <- function(x, times=2) { 130 | if (!is.vector(x)) 131 | stop("Param x should be a vector.") 132 | as.vector(sapply(x, rep, times)) 133 | } 134 | 135 | #' Vector binning 136 | #' 137 | #' Bin a vector into `n` intervals in regard with its value range. 138 | #' The vector x is split into n bins within [min(x), max(x)], 139 | #' and bin index is given by checking the bin [bin_min, bin_max) 140 | #' into which data points in x fall. 141 | #' 142 | #' @param x a numeric vector 143 | #' @param n the number of bins 144 | #' @param center indication of representing intervals as index (FALSE, default) or 145 | #' center points (TRUE). 146 | #' @return Sequence with interval index or center points. 147 | #' @seealso \code{\link{seq_approximate}}, \code{\link{vbin.range}}, \code{\link{vbin.grid}} 148 | #' @export 149 | #' @examples 150 | #' vbin(1:10, 3) 151 | #' vbin(1:10, 3, TRUE) 152 | vbin <- function(x, n, center=FALSE){ 153 | x.bin <- seq(floor(min(x)), ceiling(max(x)), length.out=n+1) 154 | x.int <- findInterval(x, x.bin, all.inside = TRUE) 155 | if(center) 156 | return(0.5 * (x.bin[x.int] + x.bin[x.int+1])) 157 | else 158 | return(x.int) 159 | } 160 | 161 | #' Vector range binning 162 | #' 163 | #' Bin the range of given vector into n intervals. 164 | #' 165 | #' @param x a numeric vector 166 | #' @param n the number of bins 167 | #' @return the center of each interval 168 | #' @export 169 | #' @seealso \code{\link{seq_approximate}}, \code{\link{vbin}}, \code{\link{vbin.grid}} 170 | #' @examples 171 | #' vbin.range(10:20, 3) 172 | vbin.range <- function(x, n){ 173 | x.bin <- seq(floor(min(x)), ceiling(max(x)), length.out=n+1) 174 | vbin(x.bin, n, TRUE)[1:n] 175 | } 176 | 177 | #' 2D random field binning 178 | #' 179 | #' Generate a bined matrix given a 2D random field. 180 | #' 181 | #' @param x,y,z a random field with location vectors (x, y) and value vector z. 182 | #' They must have the same length. 183 | #' @param nx,ny the number of bins in x and y dimension. 184 | #' @param FUN a function to calculate statistics in each 2D bin. 185 | #' @param na Replacement for NA value in matrix bins. 186 | #' @return a matrix with row (column) names being the center points of x (y) dim, 187 | #' and with cell value being the aggregate statistics calculated by FUN. 188 | #' @seealso \code{\link{seq_approximate}}, \code{\link{vbin}}, \code{\link{vbin.range}} 189 | #' @export 190 | #' @examples 191 | #' vbin.grid(1:20, 20:1, runif(20), nx=5, ny=5) 192 | vbin.grid <- function(x, y, z, nx, ny, FUN=mean, na=NA){ 193 | if( length(x) != length(y) || length(x) != length(z)) 194 | stop("Input x, y, z should be the same legnth.") 195 | 196 | # generate binning sequence of x and y 197 | x.int <- vbin(x, nx) 198 | y.int <- vbin(y, ny) 199 | 200 | # binning matrix statistics 201 | df <- data.frame(x.int=x.int, y.int=y.int, z=z) 202 | dfg <- df %>% 203 | dplyr::group_by(.data$x.int, .data$y.int) %>% 204 | dplyr::summarise(z = FUN(.data$z)) 205 | 206 | mat <- matrix(data=na, nrow=nx, ncol=ny) 207 | mat[cbind(dfg$x.int, dfg$y.int)] <- dfg$z 208 | rownames(mat) <- vbin.range(x, nx) 209 | colnames(mat) <- vbin.range(y, ny) 210 | mat 211 | } -------------------------------------------------------------------------------- /docs/source/examples.rst: -------------------------------------------------------------------------------- 1 | Examples and Tutorials 2 | ====================== 3 | 4 | This section provides detailed examples and tutorials for using `movr`. 5 | 6 | 3D Trajectory Visualization 7 | --------------------------- 8 | 9 | Visualize individual or group mobility patterns in 3D space: 10 | 11 | .. code-block:: r 12 | 13 | library(movr) 14 | data(movement) 15 | 16 | # 3D trajectory plot with color coding by user 17 | plot_traj3d(movement, 18 | x = "lon", y = "lat", z = "timestamp", 19 | color_by = "user_id", 20 | alpha = 0.7, 21 | size = 2) 22 | 23 | # Voronoi tessellation in 3D 24 | voronoi_result <- voronoi3d(movement, 25 | x = "lon", y = "lat", z = "timestamp") 26 | 27 | Flow Maps 28 | --------- 29 | 30 | Analyze population movements and migration patterns: 31 | 32 | .. code-block:: r 33 | 34 | # Create flow map from mobility data 35 | flow_data <- flowmap(movement, 36 | from = "origin_cell", 37 | to = "destination_cell", 38 | weight = "flow_count") 39 | 40 | # Visualize with custom styling 41 | plot_flowmap(flow_data, 42 | node_size = "population", 43 | edge_width = "flow_strength", 44 | color_scheme = "viridis", 45 | alpha = 0.8) 46 | 47 | # Flow statistics 48 | flow_stats <- flow.stat(flow_data) 49 | 50 | Spatial Analysis 51 | --------------- 52 | 53 | Perform various spatial analyses: 54 | 55 | .. code-block:: r 56 | 57 | # Calculate radius of gyration for each user 58 | rog <- radius_of_gyration(movement, 59 | x = "lon", y = "lat", 60 | id = "user_id") 61 | 62 | # Spatial correlation analysis 63 | spatial_corr <- spatial.corr(movement, 64 | x = "lon", y = "lat", 65 | time_window = "daily") 66 | 67 | # Point coverage analysis 68 | coverage <- point.coverage(movement, 69 | x = "lon", y = "lat", 70 | radius = 1000) # 1km radius 71 | 72 | # People occurrence analysis 73 | occurrence <- people.occurrence(movement, 74 | x = "lon", y = "lat", 75 | id = "user_id") 76 | 77 | Temporal Analysis 78 | ---------------- 79 | 80 | Analyze temporal patterns in mobility data: 81 | 82 | .. code-block:: r 83 | 84 | # Time-of-day analysis 85 | tod_data <- hour2tod(movement$timestamp) 86 | 87 | # Time-of-week analysis 88 | tow_data <- hour2tow(movement$timestamp) 89 | 90 | # Generate mobility sessions 91 | sessions <- gen_sessions(movement, 92 | id = "user_id", 93 | time_threshold = 3600) # 1 hour 94 | 95 | # Calculate temporal entropy 96 | temp_entropy <- entropy.spacetime(movement, 97 | id = "user_id", 98 | time_bins = 24) 99 | 100 | # Spatial entropy 101 | spatial_entropy <- entropy.space(movement, 102 | id = "user_id", 103 | x = "lon", y = "lat") 104 | 105 | # Random entropy 106 | random_entropy <- entropy.rand(movement, 107 | id = "user_id") 108 | 109 | Statistical Analysis 110 | ------------------- 111 | 112 | Perform statistical analyses on mobility patterns: 113 | 114 | .. code-block:: r 115 | 116 | # Fit power law distribution 117 | power_law <- fit.power.law(movement$distance) 118 | 119 | # Fit truncated power law 120 | trunc_power_law <- fit.truncated.power.law(movement$distance) 121 | 122 | # Fit polyexponential distribution 123 | polyexp <- fit.polyexp(movement$distance) 124 | 125 | # Calculate RMSE 126 | rmse_value <- RMSE(predicted, observed) 127 | 128 | Data Quality Assessment 129 | ---------------------- 130 | 131 | Assess and validate your mobility data: 132 | 133 | .. code-block:: r 134 | 135 | # Comprehensive trajectory quality check 136 | dq_result <- dq.traj(movement, 137 | id = "user_id", 138 | time = "timestamp", 139 | x = "lon", y = "lat") 140 | 141 | # Point-level quality assessment 142 | point_quality <- dq.point(movement, 143 | x = "lon", y = "lat", 144 | time = "timestamp") 145 | 146 | # Iovan distance quality check 147 | iovan_quality <- dq.iovan(movement, 148 | x = "lon", y = "lat", 149 | time = "timestamp") 150 | 151 | Coordinate Transformations 152 | ------------------------- 153 | 154 | Convert between different coordinate systems: 155 | 156 | .. code-block:: r 157 | 158 | # Cartesian to geographic coordinates 159 | geo_coords <- cart2geo(x_cart, y_cart) 160 | 161 | # Geographic to Cartesian coordinates 162 | cart_coords <- geo2cart(lon, lat) 163 | 164 | # Radian conversions 165 | geo_rad <- cart2geo.radian(x_cart, y_cart) 166 | cart_rad <- geo2cart.radian(lon_rad, lat_rad) 167 | 168 | # Degree to radian conversion 169 | radians <- deg2rad(degrees) 170 | degrees <- rad2deg(radians) 171 | 172 | Utility Functions 173 | ---------------- 174 | 175 | Use various utility functions: 176 | 177 | .. code-block:: r 178 | 179 | # Calculate great circle distance 180 | distance <- gcd(lat1, lon1, lat2, lon2) 181 | 182 | # Euclidean distance 183 | euc_dist <- euc.dist(x1, y1, x2, y2) 184 | 185 | # Pairwise distances 186 | pairwise_dist <- pairwise.dist(coordinates) 187 | 188 | # Find midpoint 189 | mid_point <- midpoint(x1, y1, x2, y2) 190 | 191 | # Check if points are in area 192 | in_area_result <- in.area(points, area_polygon) 193 | 194 | # Rotate matrix 90 degrees 195 | rotated_matrix <- rot90(matrix_data) 196 | 197 | # Repeat each element 198 | repeated <- rep_each(vector, times) 199 | 200 | Sequence Analysis 201 | ---------------- 202 | 203 | Analyze mobility sequences: 204 | 205 | .. code-block:: r 206 | 207 | # Approximate sequence 208 | approx_seq <- seq_approximate(sequence, tolerance) 209 | 210 | # Collapse sequence 211 | collapsed_seq <- seq_collapsed(sequence) 212 | 213 | # Distinct sequence 214 | distinct_seq <- seq_distinct(sequence) 215 | 216 | # Sequence distance 217 | seq_distance <- seq_dist(seq1, seq2) 218 | 219 | Visualization Gallery 220 | -------------------- 221 | 222 | Here are some example visualizations you can create with `movr`: 223 | 224 | 3D Mobility Trajectories 225 | ~~~~~~~~~~~~~~~~~~~~~~~~ 226 | 227 | .. image:: ../../examples/mobility3d.png 228 | :alt: 3D Trajectories 229 | :align: center 230 | 231 | Flow Maps 232 | ~~~~~~~~~ 233 | 234 | .. image:: ../../examples/flowmap.png 235 | :alt: Flow Maps 236 | :align: center 237 | 238 | 3D Map Layers 239 | ~~~~~~~~~~~~~ 240 | 241 | .. image:: ../../examples/map3d-rgl.png 242 | :alt: 3D Maps 243 | :align: center 244 | 245 | Advanced Examples 246 | ---------------- 247 | 248 | For more advanced examples and use cases, see the vignettes: 249 | 250 | .. code-block:: r 251 | 252 | # View available vignettes 253 | vignette(package = "movr") 254 | 255 | # Open specific vignette 256 | vignette("vignette_name", package = "movr") -------------------------------------------------------------------------------- /docs/source/contributing.rst: -------------------------------------------------------------------------------- 1 | Contributing to movr 2 | =================== 3 | 4 | Thank you for your interest in contributing to `movr`! This guide will help you get started with contributing to the project. 5 | 6 | Getting Started 7 | -------------- 8 | 9 | **Prerequisites** 10 | * R >= 3.0.0 11 | * GNU CMake 12 | * GLib development libraries 13 | * Git 14 | 15 | **Fork and Clone** 16 | Fork the repository on GitHub and clone your fork: 17 | 18 | .. code-block:: bash 19 | 20 | git clone https://github.com/YOUR_USERNAME/movr.git 21 | cd movr 22 | 23 | **Install Dependencies** 24 | Install the required system dependencies: 25 | 26 | **Ubuntu/Debian:** 27 | .. code-block:: bash 28 | 29 | sudo apt-get install cmake build-essential libglib2.0-dev 30 | 31 | **macOS:** 32 | .. code-block:: bash 33 | 34 | brew install cmake glib 35 | 36 | Development Setup 37 | ---------------- 38 | 39 | **Install Development Dependencies** 40 | Install R development packages: 41 | 42 | .. code-block:: r 43 | 44 | install.packages(c("devtools", "roxygen2", "testthat", "knitr")) 45 | 46 | **Build the Package** 47 | Build the package from source: 48 | 49 | .. code-block:: bash 50 | 51 | # Build C source 52 | ./configure 53 | 54 | # Install the package 55 | R --no-save -e "library(devtools);install()" 56 | 57 | **Run Tests** 58 | Run the test suite: 59 | 60 | .. code-block:: r 61 | 62 | library(testthat) 63 | test_package("movr") 64 | 65 | Code Style 66 | ---------- 67 | 68 | **R Code Style** 69 | Follow the R style guide: 70 | * Use meaningful variable names 71 | * Add comments for complex logic 72 | * Use consistent indentation (2 spaces) 73 | * Follow R naming conventions 74 | 75 | **C Code Style** 76 | For C extensions: 77 | * Use meaningful function and variable names 78 | * Add comments for complex algorithms 79 | * Follow C99 standard 80 | * Use consistent indentation 81 | 82 | **Documentation** 83 | * Update function documentation using roxygen2 84 | * Add examples to function documentation 85 | * Update README.md for user-facing changes 86 | * Update this documentation for developer-facing changes 87 | 88 | Making Changes 89 | -------------- 90 | 91 | **Create a Branch** 92 | Create a feature branch for your changes: 93 | 94 | .. code-block:: bash 95 | 96 | git checkout -b feature/your-feature-name 97 | 98 | **Make Your Changes** 99 | * Write your code 100 | * Add tests for new functionality 101 | * Update documentation 102 | * Ensure all tests pass 103 | 104 | **Test Your Changes** 105 | Run the full test suite: 106 | 107 | .. code-block:: bash 108 | 109 | ./scripts/check_cran.sh --quick 110 | 111 | Or run individual checks: 112 | 113 | .. code-block:: bash 114 | 115 | R CMD build . 116 | R CMD check movr_*.tar.gz 117 | 118 | **Commit Your Changes** 119 | Use descriptive commit messages: 120 | 121 | .. code-block:: bash 122 | 123 | git add . 124 | git commit -m "Add new function for spatial analysis" 125 | git push origin feature/your-feature-name 126 | 127 | **Create a Pull Request** 128 | * Go to the GitHub repository 129 | * Click "New Pull Request" 130 | * Select your feature branch 131 | * Fill out the pull request template 132 | * Submit the pull request 133 | 134 | Pull Request Guidelines 135 | ---------------------- 136 | 137 | **Before Submitting** 138 | * Ensure all tests pass 139 | * Update documentation 140 | * Add examples for new functions 141 | * Check that the package builds successfully 142 | * Verify OS compatibility (Linux and macOS only) 143 | 144 | **Pull Request Template** 145 | Include the following information: 146 | 147 | * Description of changes 148 | * Related issue number (if applicable) 149 | * Type of change (bug fix, feature, documentation) 150 | * Testing performed 151 | * OS tested on 152 | 153 | **Review Process** 154 | * All pull requests require review 155 | * Address reviewer comments 156 | * Ensure CI/CD checks pass 157 | * Maintain backward compatibility 158 | 159 | Testing 160 | ------- 161 | 162 | **Writing Tests** 163 | Add tests for new functionality: 164 | 165 | .. code-block:: r 166 | 167 | test_that("new_function works correctly", { 168 | # Test setup 169 | data <- create_test_data() 170 | 171 | # Test function 172 | result <- new_function(data) 173 | 174 | # Assertions 175 | expect_equal(length(result), 10) 176 | expect_true(all(result > 0)) 177 | }) 178 | 179 | **Running Tests** 180 | Run tests during development: 181 | 182 | .. code-block:: r 183 | 184 | library(testthat) 185 | test_file("tests/testthat/test-your-function.R") 186 | 187 | **Test Coverage** 188 | Aim for high test coverage for new functions. 189 | 190 | Documentation 191 | ------------- 192 | 193 | **Function Documentation** 194 | Use roxygen2 for function documentation: 195 | 196 | .. code-block:: r 197 | 198 | #' Title of the function 199 | #' 200 | #' @param x Description of parameter x 201 | #' @param y Description of parameter y 202 | #' @return Description of return value 203 | #' @examples 204 | #' # Example usage 205 | #' result <- my_function(x, y) 206 | #' @export 207 | my_function <- function(x, y) { 208 | # Function implementation 209 | } 210 | 211 | **Package Documentation** 212 | Update package documentation: 213 | 214 | .. code-block:: r 215 | 216 | #' @title Package Title 217 | #' @description Package description 218 | #' @docType package 219 | #' @name movr 220 | NULL 221 | 222 | **Vignettes** 223 | Create vignettes for complex functionality: 224 | 225 | .. code-block:: r 226 | 227 | #' @title Vignette Title 228 | #' @author Your Name 229 | #' @description Description of the vignette 230 | #' @keywords internal 231 | NULL 232 | 233 | Release Process 234 | -------------- 235 | 236 | **Version Bumping** 237 | Update version numbers in: 238 | * `DESCRIPTION` file 239 | * `docs/source/conf.py` 240 | * `README.md` (if needed) 241 | 242 | **Release Checklist** 243 | * All tests pass 244 | * Documentation is updated 245 | * Examples work correctly 246 | * Package builds successfully 247 | * OS compatibility verified 248 | * CRAN requirements met 249 | 250 | **CRAN Submission** 251 | Before submitting to CRAN: 252 | * Run `R CMD check --as-cran` 253 | * Fix all warnings and errors 254 | * Test on multiple R versions 255 | * Verify all dependencies are available 256 | 257 | Getting Help 258 | ----------- 259 | 260 | **Questions and Issues** 261 | * Check existing issues on GitHub 262 | * Search the documentation 263 | * Ask questions in GitHub issues 264 | * Contact the maintainers 265 | 266 | **Development Resources** 267 | * `R Development Guide `_ 268 | * `R Package Development `_ 269 | * `testthat Documentation `_ 270 | * `roxygen2 Documentation `_ 271 | 272 | **Community Guidelines** 273 | * Be respectful and inclusive 274 | * Provide constructive feedback 275 | * Help others learn and contribute 276 | * Follow the project's code of conduct 277 | 278 | Thank you for contributing to `movr`! -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 16 | 17 | .PHONY: help 18 | help: 19 | @echo "Please use \`make ' where is one of" 20 | @echo " html to make standalone HTML files" 21 | @echo " dirhtml to make HTML files named index.html in directories" 22 | @echo " singlehtml to make a single large HTML file" 23 | @echo " pickle to make pickle files" 24 | @echo " json to make JSON files" 25 | @echo " htmlhelp to make HTML files and a HTML help project" 26 | @echo " qthelp to make HTML files and a qthelp project" 27 | @echo " applehelp to make an Apple Help Book" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " epub3 to make an epub3" 31 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 32 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 33 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 34 | @echo " text to make text files" 35 | @echo " man to make manual pages" 36 | @echo " texinfo to make Texinfo files" 37 | @echo " info to make Texinfo files and run them through makeinfo" 38 | @echo " gettext to make PO message catalogs" 39 | @echo " changes to make an overview of all changed/added/deprecated items" 40 | @echo " xml to make Docutils-native XML files" 41 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 42 | @echo " linkcheck to check all external links for integrity" 43 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 44 | @echo " coverage to run coverage check of the documentation (if enabled)" 45 | @echo " dummy to check syntax errors of document sources" 46 | 47 | .PHONY: clean 48 | clean: 49 | rm -rf $(BUILDDIR)/* 50 | 51 | .PHONY: html 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | .PHONY: dirhtml 58 | dirhtml: 59 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 60 | @echo 61 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 62 | 63 | .PHONY: singlehtml 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | .PHONY: pickle 70 | pickle: 71 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 72 | @echo 73 | @echo "Build finished; now you can process the pickle files." 74 | 75 | .PHONY: json 76 | json: 77 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 78 | @echo 79 | @echo "Build finished; now you can process the JSON files." 80 | 81 | .PHONY: htmlhelp 82 | htmlhelp: 83 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 84 | @echo 85 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 86 | ".hhp project file in $(BUILDDIR)/htmlhelp." 87 | 88 | .PHONY: qthelp 89 | qthelp: 90 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 91 | @echo 92 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 93 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 94 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/movr.qhcp" 95 | @echo "To view the help file:" 96 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/movr.qhc" 97 | 98 | .PHONY: applehelp 99 | applehelp: 100 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 101 | @echo 102 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 103 | @echo "N.B. You won't be able to view it unless you put it in" \ 104 | "~/Library/Documentation/Help or install it in your application" \ 105 | "bundle." 106 | 107 | .PHONY: devhelp 108 | devhelp: 109 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 110 | @echo 111 | @echo "Build finished." 112 | @echo "To view the help file:" 113 | @echo "# mkdir -p $$HOME/.local/share/devhelp/movr" 114 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/movr" 115 | @echo "# devhelp" 116 | 117 | .PHONY: epub 118 | epub: 119 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 120 | @echo 121 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 122 | 123 | .PHONY: epub3 124 | epub3: 125 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 126 | @echo 127 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." 128 | 129 | .PHONY: latex 130 | latex: 131 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 132 | @echo 133 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 134 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 135 | "(use \`make latexpdf' here to do that automatically)." 136 | 137 | .PHONY: latexpdf 138 | latexpdf: 139 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 140 | @echo "Running LaTeX files through pdflatex..." 141 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 142 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 143 | 144 | .PHONY: latexpdfja 145 | latexpdfja: 146 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 147 | @echo "Running LaTeX files through platex and dvipdfmx..." 148 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 149 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 150 | 151 | .PHONY: text 152 | text: 153 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 154 | @echo 155 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 156 | 157 | .PHONY: man 158 | man: 159 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 160 | @echo 161 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 162 | 163 | .PHONY: texinfo 164 | texinfo: 165 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 166 | @echo 167 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 168 | @echo "Run \`make' in that directory to run these through makeinfo" \ 169 | "(use \`make info' here to do that automatically)." 170 | 171 | .PHONY: info 172 | info: 173 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 174 | @echo "Running Texinfo files through makeinfo..." 175 | make -C $(BUILDDIR)/texinfo info 176 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 177 | 178 | .PHONY: gettext 179 | gettext: 180 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 181 | @echo 182 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 183 | 184 | .PHONY: changes 185 | changes: 186 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 187 | @echo 188 | @echo "The overview file is in $(BUILDDIR)/changes." 189 | 190 | .PHONY: linkcheck 191 | linkcheck: 192 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 193 | @echo 194 | @echo "Link check complete; look for any errors in the above output " \ 195 | "or in $(BUILDDIR)/linkcheck/output.txt." 196 | 197 | .PHONY: doctest 198 | doctest: 199 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 200 | @echo "Testing of doctests in the sources finished, look at the " \ 201 | "results in $(BUILDDIR)/doctest/output.txt." 202 | 203 | .PHONY: coverage 204 | coverage: 205 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 206 | @echo "Testing of coverage in the sources finished, look at the " \ 207 | "results in $(BUILDDIR)/coverage/python.txt." 208 | 209 | .PHONY: xml 210 | xml: 211 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 212 | @echo 213 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 214 | 215 | .PHONY: pseudoxml 216 | pseudoxml: 217 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 218 | @echo 219 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 220 | 221 | .PHONY: dummy 222 | dummy: 223 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy 224 | @echo 225 | @echo "Build finished. Dummy builder generates no files." 226 | --------------------------------------------------------------------------------