Creating Spatial Resources for World Bank Survey Solutions Fieldwork
Source:vignettes/survey-solutions-workflow.Rmd
survey-solutions-workflow.RmdOverview
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. |
Create .zip archive for upload
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.
1. Delineating Enumeration Areas, Sample Points &
.tab Assignment Preloads
Survey Solutions Headquarters requires
tab-delimited (.tab) preload files with
specific column naming conventions:
-
<gps_var>__Latitudeand<gps_var>__Longitude: To display sampled households as interactive pins on the Interviewer tablet’s Map Dashboard (overlaid directly on top of the offline.tpkbasemap), your questionnaire in Survey Solutions Designer should include an Identifying Geography (GPS) question (e.g., variable namehh_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, andhh_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 asIdentifyingtext questions on the questionnaire cover page. Because.tpksatellite 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.tpkbasemap for the final approach. Meanwhile,imagery_acq_datetells 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)
#> Linking to GEOS 3.12.1, GDAL 3.8.4, PROJ 9.4.0; sf_use_s2() is TRUE
# 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 C22. Creating a Zoomable .tpk Basemap
(vantor_write_tpk())
Choosing Zoom Levels to Conserve Vantor Credits
Each Web Mercator zoom level quadruples the number of pixel tiles required to cover an Area of Interest:
- Zoom 15 (, Scale ): Neighborhood context overview.
- Zoom 16 (, Scale ): Enumeration Area and road network view.
- Zoom 17 (, Scale ): Building block and compound level.
- Zoom 18 (, Scale ): Individual rooftop and dwelling identification.
- Zoom 19 (, Scale ): Full native 30 cm Vantor Vivid resolution.
For testing or small Enumeration Areas
(),
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.
Online Workflow (Direct from Vantor WMS)
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
)Offline Demonstration (From a Local 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"Understanding WKID: 102100, MinScale, and
MaxScale in Survey Solutions HQ
Once 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 forWGS_1984_Web_Mercator_Auxiliary_Sphere(EPSG:3857). Inmapserver.json, Esri stores"spatialReference": { "wkid": 102100, "latestWkid": 3857 }. Survey Solutions Headquarters readsfullExtent.spatialReference.wkid(102100) and uses{"wkid": 102100}in its ArcGIS JS MapPreview widget. Spatially,102100and3857are identical. -
MinScaleandMaxScale: Forzoom = 15:17, Survey Solutions reportsMinScale = 18055.99(zoom 15 overview scale) andMaxScale = 4513.99(zoom 17 close-up scale). On the interviewer tablet, Survey Solutions configures theArcGISTiledLayerso enumerators can pan and zoom smoothly across the cached pyramid.
3. One-Call Bundling: .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:
- Populates the
labelattribute column ongrid(fromcell_id, e.g.,"A1","A2") andsamples(fromsample_id, e.g.,"EA01-HH01"). - Exports
EPSG:4326Shapefiles and packages them into<package_name>_shapes.zipat the root level (no subfolders, no__MACOSXforks). - Generates
<package_name>_suso_preload.tab(with<suso_gps_var>__Latitude,<suso_gps_var>__Longitude, and_target_area) alongside<package_name>_samples.csv. - Reprojects any raster basemap to Web Mercator
(
EPSG:3857).tif. - Generates both
<package_name>.tpkand<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"4. Manual Upload via the Survey Solutions HQ Web Interface
- Log in to Survey Solutions Headquarters as an
AdministratororHeadquarteruser. -
Upload Maps: Navigate to Survey Setup
Maps, click Upload Map, and select
suso_ea01_tpk.zip(and optionallysuso_ea01_shapes.zip). Click on the uploaded map entry to verify its bounding box in Map Preview and confirm itsMinScale,MaxScale, andWKID(102100). -
Upload Preloaded Assignments: Navigate to
Survey Setup
Questionnaires, click your survey questionnaire, select
Upload Assignments, and upload
suso_ea01_suso_preload.tab. -
Bulk-Assign Maps to Interviewers
(
usermaps.tab): You can link uploaded.tpkand.shpmaps to specific Interviewer accounts by uploading a tab-separatedusermaps.tabfile (Survey Setup -> Maps -> Upload User-to-Map Assignments) with two columns (mapandusers):
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_025. Automated End-to-End Deployment with
SurveySolutionsAPIv2
Instead 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).