World Bank Survey Solutions supports offline satellite basemaps and vector boundary overlays on Interviewer and Supervisor tablets (powered by the ArcGIS Maps SDK for .NET v200.x from Survey Solutions v24.09 onwards).
However, preparing satellite imagery and vector files that pass
Survey Solutions Headquarters server-side validation
(Survey Setup -> Maps -> Upload Map) and render
accurately on offline Android tablets requires strict adherence to Esri
and Survey Solutions packaging conventions:
| Resource Type | Format Uploaded to Survey Solutions HQ | Required Coordinate System | Key Survey Solutions Requirements |
|---|---|---|---|
| Zoomable Tile Basemap | .tpk inside a flat .zip
(<name>_tpk.zip) |
Web Mercator (WKID: 102100 /
EPSG:3857) |
Must contain v101/Layers/conf.xml,
conf.cdi, esriinfo/iteminfo.xml, and
servicedescriptions/mapserver/mapserver.json with a
top-level "contents" object (fullExtent,
minScale, maxScale). |
| Single-Resolution Raster Basemap | .tif inside a flat .zip |
Web Mercator (EPSG:3857 /
WGS 84) |
Valid GeoTIFF header tags
(GeoTiffInfoReader); automatically reprojected to
EPSG:3857 by vantor_offline_package(). |
| Vector Boundary / Point Overlay | .shp, .shx,
.dbf, .prj inside a flat .zip
(<name>_shapes.zip) |
Geographic WGS 84
(EPSG:4326) |
Mandatory .shp, .shx,
.dbf at the archive root; an optional label
attribute column is rendered automatically as feature labels on the
tablet map. |
| Assignment Preload Table | Tab-delimited .tab
(<name>_suso_preload.tab) |
Geographic WGS 84
(EPSG:4326) |
Composite <gps_var>__Latitude &
<gps_var>__Longitude columns for Map
Dashboard household pins, _target_area (from
cell_id), plus_code for offline cover-page
routing, and imagery_acq_date. |
vantorr handles all of the upload-requirements
automatically in pure R—without requiring ArcGIS Pro, ArcMap, or ArcGIS
Online credits—using vantor_write_tpk(),
vantor_export_capi(), and
vantor_offline_package(), and integrates directly with the
SurveySolutionsAPIv2
package for programmatic server uploads and assignment creation.
.tab Assignment PreloadsSurvey Solutions Headquarters requires
tab-delimited (.tab) preload files with
specific column naming conventions:
<gps_var>__Latitude and
<gps_var>__Longitude: To display sampled
households as interactive pins on the Interviewer tablet’s Map
Dashboard (overlaid directly on top of the offline
.tpk basemap), your questionnaire in Survey Solutions
Designer should include an Identifying Geography
(GPS) question (e.g., variable name hh_gps).
Survey Solutions preloads GPS questions via composite column headers
separated by a double underscore (__):
hh_gps__Latitude, hh_gps__Longitude,
hh_gps__Accuracy, hh_gps__Altitude, and
hh_gps__Timestamp._target_area: Survey Solutions’ system
column (up to 64 characters) that groups assignments by geographic
sector (cell_id, e.g., "A1",
"B2") on the Interviewer Map Dashboard.plus_code &
imagery_acq_date: Preloaded as
Identifying text questions on the questionnaire cover page.
Because .tpk satellite basemaps do not perform turn-by-turn
road routing, enumerators can copy the 11-character Open Location Code
(plus_code) from the cover page into Google Maps or OSM to
get offline turn-by-turn driving/walking directions to within 3 meters
of the dwelling in case they are not using preloaded GPS coordinates,
then switch back to the Survey Solutions Map Dashboard and
.tpk basemap for the final approach. Meanwhile,
imagery_acq_date tells the enumerator when the Vantor
satellite image was captured so they can distinguish newly constructed
buildings from older structures, which may be useful in household
enumeration operations.Specifying output_suso_tab and
suso_gps_var = "hh_gps" in
vantor_export_capi() generates this exact .tab
file automatically:
library(vantorr)
library(sf)
# Define a small Enumeration Area bounding box in WGS84 (EPSG:4326)
ea_aoi <- st_bbox(
c(xmin = -105.004, ymin = 39.736, xmax = -105.000, ymax = 39.740),
crs = st_crs(4326)
)
# 1. Generate 250m Enumeration Area sub-cells (A1, A2, B1, B2)
ea_grid <- vantor_survey_grid(
aoi = ea_aoi,
cell_size = 250,
shape = "square",
verbose = FALSE
)
# 2. Define sampled household locations inside the EA
hh_samples <- st_as_sf(
data.frame(
hh_id = c("EA01-HH01", "EA01-HH02", "EA01-HH03"),
lon = c(-105.0030, -105.0015, -105.0010),
lat = c(39.7388, 39.7382, 39.7370)
),
coords = c("lon", "lat"),
crs = 4326
)
suso_tab_file <- tempfile(pattern = "suso_assignments_", fileext = ".tab")
capi_pts <- vantor_export_capi(
samples = hh_samples,
id_col = "hh_id",
grid = ea_grid,
output_suso_tab = suso_tab_file,
suso_gps_var = "hh_gps",
verbose = FALSE
)
# Inspect the returned sf object (coordinate columns use <suso_gps_var>__Latitude / __Longitude)
st_drop_geometry(capi_pts)[, c(
"sample_id", "cell_id", "plus_code",
"hh_gps__Latitude", "hh_gps__Longitude"
)]
#> sample_id cell_id plus_code hh_gps__Latitude hh_gps__Longitude
#> 1 EA01-HH01 B1 85FPPXQW+GR 39.7388 -105.0030
#> 2 EA01-HH02 B2 85FPPXQX+7C 39.7382 -105.0015
#> 3 EA01-HH03 C2 85FPPXPX+RJ 39.7370 -105.0010
# Inspect the generated Survey Solutions .tab preload table
read.delim(suso_tab_file, check.names = FALSE)[, c(
"sample_id", "cell_id", "plus_code",
"hh_gps__Latitude", "hh_gps__Longitude",
"_target_area"
)]
#> sample_id cell_id plus_code hh_gps__Latitude hh_gps__Longitude _target_area
#> 1 EA01-HH01 B1 85FPPXQW+GR 39.7388 -105.0030 B1
#> 2 EA01-HH02 B2 85FPPXQX+7C 39.7382 -105.0015 B2
#> 3 EA01-HH03 C2 85FPPXPX+RJ 39.7370 -105.0010 C2.tpk Basemap
(vantor_write_tpk())Each Web Mercator zoom level quadruples the number of \(256 \times 256\) pixel tiles required to cover an Area of Interest:
For testing or small Enumeration Areas (\(\approx 200\text{ m} \times 200\text{
m}\)), zoom = 15:17 requires only 6 to 9
tiles in total. vantor_write_tpk() enforces a
safety cap (max_tiles = 2500L) so you never accidentally
trigger a massive download.
When authenticated via vantor_auth(), pass your AOI
(x or aoi) and set
zip_for_suso = TRUE:
# Authenticate with your Vantor API Key
vantor_auth(api_key = Sys.getenv("VANTOR_API_KEY"))
# Define a compact ~200m x 200m test AOI
tiny_aoi <- st_bbox(
c(xmin = -105.002, ymin = 39.738, xmax = -105.000, ymax = 39.740),
crs = st_crs(4326)
)
# Generate both test_vantor_suso.tpk and the clean test_vantor_suso_tpk.zip upload archive
tpk_file <- vantor_write_tpk(
aoi = tiny_aoi,
output_file = "test_vantor_suso.tpk",
zoom = 15:17,
format = "tpk",
product = "VIVID_STANDARD_30",
zip_for_suso = TRUE
)SpatRaster /
GeoTIFF)If you have already downloaded a GeoTIFF using
get_maxar_wms_basemap(), you can slice it offline into a
.tpk package without making any additional API
requests:
if (requireNamespace("terra", quietly = TRUE)) {
# Create a synthetic 3-band RGB raster over our EA (in WGS84)
r_ea <- terra::rast(
xmin = -105.004, xmax = -105.000,
ymin = 39.736, ymax = 39.740,
nrows = 128, ncols = 128, nlyrs = 3,
crs = "EPSG:4326"
)
set.seed(42)
terra::values(r_ea) <- sample(60:220, terra::ncell(r_ea) * 3, replace = TRUE)
out_tpk <- file.path(tempdir(), "ea_01_basemap.tpk")
tpk_res <- vantor_write_tpk(
raster = r_ea,
output_file = out_tpk,
zoom = 15:16,
format = "tpk",
zip_for_suso = TRUE,
verbose = FALSE
)
# The clean Survey Solutions upload archive is named *_tpk.zip:
suso_zip <- attr(tpk_res, "suso_zip_path")
basename(c(tpk_res, suso_zip))
# Verify the outer _tpk.zip contains only the .tpk at the root (no __MACOSX metadata)
utils::unzip(suso_zip, list = TRUE)$Name
# Verify the internal Esri Compact Cache V1 structure inside the .tpk
utils::unzip(tpk_res, list = TRUE)$Name
}
#> [1] "esriinfo/item.pkinfo"
#> [2] "esriinfo/iteminfo.xml"
#> [3] "servicedescriptions/mapserver/mapserver.json"
#> [4] "v101/Layers/_alllayers/L15/R0000C0000.bundle"
#> [5] "v101/Layers/_alllayers/L15/R0000C0000.bundlx"
#> [6] "v101/Layers/_alllayers/L16/R0000C0000.bundle"
#> [7] "v101/Layers/_alllayers/L16/R0000C0000.bundlx"
#> [8] "v101/Layers/conf.cdi"
#> [9] "v101/Layers/conf.xml"WKID: 102100, MinScale, and
MaxScale in Survey Solutions HQOnce you upload ea_01_basemap_tpk.zip to Survey
Solutions Headquarters
(Survey Setup -> Maps -> Upload Map) and click on the
uploaded map to view its Details / Preview, you will
see metadata extracted from
servicedescriptions/mapserver/mapserver.json:
WKID: 102100: This is Esri’s canonical
authority code for WGS_1984_Web_Mercator_Auxiliary_Sphere
(EPSG:3857). In mapserver.json, Esri stores
"spatialReference": { "wkid": 102100, "latestWkid": 3857 }.
Survey Solutions Headquarters reads
fullExtent.spatialReference.wkid (102100) and
uses {"wkid": 102100} in its ArcGIS JS MapPreview widget.
Spatially, 102100 and 3857 are identical.MinScale and MaxScale:
For zoom = 15:17, Survey Solutions reports
MinScale = 18055.99 (zoom 15 overview scale) and
MaxScale = 4513.99 (zoom 17 close-up scale). On the
interviewer tablet, Survey Solutions configures the
ArcGISTiledLayer so enumerators can pan and zoom smoothly
across the cached pyramid..tpk, .tif,
.tab, and Label-Ready Shapefiles
(_shapes.zip)In addition to a .tpk raster basemap, field supervisors
often overlay Enumeration Area polygon boundaries or household sample
points as Esri Shapefiles in Survey Solutions.
When Survey Solutions imports a Shapefile (.shp,
.shx, .dbf, .prj), it checks the
.dbf attribute table for a column named
label (case-insensitive). If present,
Survey Solutions renders those labels directly on top of the map in both
Headquarters Map Preview and the Interviewer app
($feature.label).
vantor_offline_package() automates this entire bundle in
a single call:
label attribute column on
grid (from cell_id, e.g., "A1",
"A2") and samples (from
sample_id, e.g., "EA01-HH01").EPSG:4326 Shapefiles and packages them into
<package_name>_shapes.zip at the root level (no
subfolders, no __MACOSX forks).<package_name>_suso_preload.tab (with
<suso_gps_var>__Latitude,
<suso_gps_var>__Longitude, and
_target_area) alongside
<package_name>_samples.csv.EPSG:3857) .tif.<package_name>.tpk and
<package_name>_tpk.zip.if (requireNamespace("terra", quietly = TRUE)) {
suso_bundle <- vantor_offline_package(
grid = ea_grid,
samples = capi_pts,
raster = r_ea,
create_tpk = "tpk",
tpk_zoom = 15:16,
create_shapefiles = TRUE,
suso_gps_var = "hh_gps",
package_name = "suso_ea01",
verbose = FALSE
)
# Files ready for Survey Solutions Headquarters upload:
basename(c(
suso_bundle$tpk_zip_path,
suso_bundle$shapefile_zip_path,
suso_bundle$suso_tab_path,
suso_bundle$raster_path,
suso_bundle$csv_path
))
# Inspect the root-level files inside suso_ea01_shapes.zip:
utils::unzip(suso_bundle$shapefile_zip_path, list = TRUE)$Name
}
#> [1] "suso_ea01_grid.dbf" "suso_ea01_grid.prj" "suso_ea01_grid.shp"
#> [4] "suso_ea01_grid.shx" "suso_ea01_samples.dbf" "suso_ea01_samples.prj"
#> [7] "suso_ea01_samples.shp" "suso_ea01_samples.shx"Administrator or Headquarter user.suso_ea01_tpk.zip (and optionally
suso_ea01_shapes.zip). Click on the
uploaded map entry to verify its bounding box in Map
Preview and confirm its MinScale,
MaxScale, and WKID (102100).suso_ea01_suso_preload.tab.usermaps.tab): You can link uploaded
.tpk and .shp maps to specific Interviewer
accounts by uploading a tab-separated usermaps.tab file
(Survey Setup -> Maps -> Upload User-to-Map Assignments)
with two columns (map and users):usermaps_df <- data.frame(
map = c("suso_ea01.tpk", "suso_ea01_grid.shp"),
users = c("interviewer_01, interviewer_02", "interviewer_01, interviewer_02"),
stringsAsFactors = FALSE
)
usermaps_tab <- file.path(tempdir(), "usermaps.tab")
utils::write.table(
usermaps_df,
file = usermaps_tab,
sep = "\t",
row.names = FALSE,
quote = FALSE
)
cat(readLines(usermaps_tab), sep = "\n")
#> map users
#> suso_ea01.tpk interviewer_01, interviewer_02
#> suso_ea01_grid.shp interviewer_01, interviewer_02SurveySolutionsAPIv2Instead of manually uploading .zip map archives and
.tab assignment files through the web browser, you can
combine vantorr with the SurveySolutionsAPIv2
R package (httr2 + GraphQL API v2 client for World Bank
Survey Solutions) to automate the entire fieldwork deployment pipeline
directly from R:
| Step | vantorr Output |
SurveySolutionsAPIv2 Function |
Action on Survey Solutions HQ Server |
|---|---|---|---|
1. Upload .tpk
Basemap |
suso_bundle$tpk_zip_path
(suso_ea01_tpk.zip) |
suso_mapupload(workspace, path_to_zip) |
Uploads & validates the .tpk pyramid
via GraphQL (suso_gql_uploadmap). |
2. Upload .shp
Overlay |
suso_bundle$shapefile_zip_path
(suso_ea01_shapes.zip) |
suso_mapupload(workspace, path_to_zip) |
Uploads Enumeration Area polygons & labeled points to HQ Maps. |
| 3. Assign Maps to Interviewers | "suso_ea01.tpk",
"suso_ea01_grid.shp" |
suso_mapassign(workspace, fileName, userName) |
Links the offline basemap and vector boundaries to the interviewer’s tablet sync queue. |
| 4. Create Preloaded Assignments | capi_pts (sample_id,
cell_id, plus_code,
imagery_acq_date) |
suso_createASS(df, questID, version) |
Creates preloaded assignments in parallel
(httr2::req_perform_parallel()). |
# Install SurveySolutionsAPIv2 from GitHub if not already installed:
# remotes::install_github("michael-cw/SurveySolutionsAPIv2")
library(SurveySolutionsAPIv2)
library(data.table)
# 1. Authenticate with Survey Solutions Headquarters
suso_set_key(
suso_server = "https://your-suso-server.mysurvey.solutions",
suso_user = Sys.getenv("SUSO_API_USER"),
suso_password = Sys.getenv("SUSO_API_PASS")
)
ws <- "primary"
# 2. Upload the Vantor .tpk basemap and Enumeration Area Shapefiles to HQ
suso_mapupload(
workspace = ws,
path_to_zip = suso_bundle$tpk_zip_path
)
suso_mapupload(
workspace = ws,
path_to_zip = suso_bundle$shapefile_zip_path
)
# Verify the uploaded maps and scales on the server
suso_mapinfo(workspace = ws)
# 3. Assign the .tpk basemap and EA grid boundary overlay to field interviewers
# (Assuming these interviewers are created on the server already)
for (int_user in c("interviewer01", "interviewer02")) {
suso_mapassign(
workspace = ws,
fileName = "suso_ea01.tpk",
userName = int_user,
assignUser = TRUE
)
suso_mapassign(
workspace = ws,
fileName = "suso_ea01_grid.shp",
userName = int_user,
assignUser = TRUE
)
}
# 4. Create preloaded assignments via REST API v1 (suso_createASS)
# (Assuming this questionnaire has the required ID fields and is loaded)
preload_dt <- data.frame(
sample_id = capi_pts$sample_id,
cell_id = capi_pts$cell_id,
plus_code = capi_pts$plus_code,
ResponsibleName = rep("interviewer01", nrow(capi_pts)),
Quantity = rep(1L, nrow(capi_pts))
)
suso_createASS(
df = preload_dt,
workspace = ws,
questID = "00000000-0000-0000-0000-000000000000",
version = 1L
)When interviewer_01 taps Synchronize on
their Android tablet: 1. Survey Solutions downloads
suso_ea01.tpk and suso_ea01_grid.shp into the
tablet’s offline Map Dashboard. 2. Each household assignment appears
with its preloaded plus_code (for offline turn-by-turn
routing in Google Maps / OSM), and cell_id
(A1, B2).
.tab
Assignment Preloads.tpk Basemap
(vantor_write_tpk()).tpk, .tif,
.tab, and Label-Ready Shapefiles
(_shapes.zip)SurveySolutionsAPIv2