At the end of this session participants will be able to:
- Use images in value sets
- View external documents and media files from a data entry application
- Take photos in a data entry application
- Capture GPS coordinates in a data entry application
- Display captured GPS points on a map
- Generate and view reports from a data entry application
In addition to showing text in a value set we can also display images. To add an image to a value set, select the variable in the dictionary editor and click on the "…" button next to the value. Browse to pick an image file. CSPro supports standard image file formats: png, jpeg, bmp… We recommend using jpeg as it tends to have smaller file sizes. The images are not added to the pen file by default so you would need to copy them to the device along with the pen file.
Let's add images for the roof types in F08. Copy the RoofImages folder into the application directory. Now open the value set for F08 and add each image to its respective value. We do not have a picture for other (specify) so we will leave that one without an image. Run the application on Windows and on mobile and see the images. Note that on mobile we have to copy the images to the device since they are not part of the pen file by default.
Instead of copying the image files to the mobile device each time they are updated we can put them in the resource directory of the application. All files in the resource folder are built into the pen file and extracted on the device when the application is run. This makes it more convenient to distribute an application with additional files. Let's create a resource folder, add it to our application and put our image files in it. To create the resource file just create a new folder in Windows explorer. We will create the folder "resources" in the household folder. Then in CSPro go to Add Files… from the File menu, choose "resource folder" and browse to the new folder. Now put the whole RoofImages folder in the resource folder and rebuild the pen file.
The function
view() can be used to launch files with their default viewer. The
view() function takes the path to the file to display. The device must have an application installed capable of viewing the type of file specified. The function determines which application to launch to view the file based on the file extension. Most devices have built-in viewers for most photo, music, and video file formats, however not all have a built-in PDF viewer. If you do not have one on your device, you can always download one. The
view() function can also display websites if you pass it a URL.
view("/mnt/sdcard/csentry/picture.jpg");
view("/mnt/sdcard/csentry/audio.mp3");
view("/mnt/sdcard/csentry/movie.mp4");
view("/mnt/sdcard/csentry/document.pdf");
view("https://www.census.gov/data/software/cspro.html");
Let's add a userbar button to show the interview manual as an aid to the interviewer :
// Display the interviewer manual
function showInterviewerManual()
view("/mnt/sdcard/csentry/Popstan2020/Housing/resources/InterviewerManual.pdf");
end;
We can make our code a little more flexible by using the function
pathname() to get the directory where the application is stored. This way if someone copies it into a different folder on the phone/tablet it will still work:
// Display the interviewer manual
function showInterviewerManual()
view(pathname(Application) + "resources/InterviewerManual.pdf");
end;
To take a picture on Android you can use the function
execsystem which is another way to launch external applications. To use
execsystem to take a photo pass it a string with "camera:" followed by the full path in which to save the photo:
execsystem("camera:/mnt/sdcard/csentry/photo.jpg");
Taking photos is currently only supported on Android.
execsystem on Windows does not support the camera.
Note that photos do not get saved into the application data file. You need to save them to a folder/file on the device. The filename you use should make it easy to link the photo back to the interview that it was taken for. The easiest way to do this is to make the name of the photo based on the id-items of the questionnaire:
string photoFilename = pathname(application) +
maketext("../Data/HouseholdPhotos/photo%v%v%v%v.jpg",
PROVINCE, DISTRICT, ENUMERATION_AREA,
HOUSEHOLD_NUMBER);
execsystem(maketext("camera:%s", photoFilename));
Note that we are putting the photo in a subdirectory of the data directory named HouseholdPhotos. We will need to create this folder on the tablet for this to work.
Let's have the interviewer take a photo of the household being interviewed in section A. Rather than adding a userbar button which would be easy for the interviewer to forget, we will make it part of the questionnaire by adding a dummy variable to the dictionary. In the postproc of the variable we will call
execsystem() to take the photo. We can add a refused code in case the household doesn't want a picture taken.
This works the first time through the questionnaire but if we go back through the photo question a second time then we are forced to take another photo. We can avoid this by adding another option to the value set "Keep photo" that the interviewer will select once they are happy with the photo that they took. When "keep photo" is selected we won't launch the camera. We can add another option "View photo" to let them look at the photo that was taken.
PROC PHOTO
string photoFilename = pathname(application) +
maketext("../Data/HouseholdPhotos /photo%v%v%v%v.jpg",
PROVINCE, DISTRICT, ENUMERATION_AREA,
HOUSEHOLD_NUMBER);
if PHOTO = 1 then
// Take/retake photo
execsystem(maketext("camera:%s", photoFilename));
// reenter so that interview can retake if it is not good.
reenter;
elseif PHOTO = 2 then
// view existing photo
view(photoFilename);
// reenter so that interview can retake if it is not good.
reenter;
elseif PHOTO = 3 then
// Keep photo - move to next field
elseif PHOTO = 9 then
// No photo (refused)
// Delete photo if it exists
filedelete(photoFilename);
endif;
This works except that it is possible for the interviewer to select "Keep photo" even when there is no existing photo and to attempt to view a photo that doesn't exist. We can solve this by only showing the "Keep photo" and "View photo" options in the value set if the photo exists already.
To capture a GPS point first you need to start up the GPS hardware:
Then you request a GPS reading giving it a timeout in seconds and optionally a desired accuracy in meters:
if gps(read, 60, 10) then // Read up to 60 seconds, try for 10m accuracy
errmsg("Latitude is %f, longitude is %f",gps(latitude),gps(longitude));
else
errmsg("GPS signal could not be acquired");
endif;
Finally, you close the GPS:
In addition to querying the GPS latitude and longitude you can also query the accuracy, number of satellites, and altitude (although on Android altitude is not very accurate). See the help on GPS for details.
The longitude and latitude reported are in degrees in the WGS84 datum and are returned as decimal numbers.
To save the GPS coordinates in our data file we need to add the appropriate variables to our dictionary. To store the coordinates so that they can cover the entire earth with sufficient precision they should have space for 3 digits before the decimal point, 1 digit for the decimal point, and at least 5 digits after the decimal and a minus sign for a minimum total length of 10. Add the variables LATITUDE and LONGITUDE to the dictionary and to section on the forms.
Let's add a user bar button to update the GPS coordinates for the household:
If we want to be fancy, we can display the point on the map first so that the interviewer can view it and then decide whether or not to use it. We can do this using a variable of type
Map. Like
valueset variables,
Map variables have functions that can be called on them using a dot ("."). For example, to add a marker to a map you would call:
To display the map, call the
show() function. This displays a Google Map containing the marker that was added.
We can use a map variable to display a map that has a marker at the GPS location that we captured:
Variables of type
Map have many other functions that let you customize the map, the markers, add buttons and run logic functions when markers are tapped. You can also use offline base maps for situations where you do not have an internet connection. See the help for maps for details. Note that map functions are only supported on Android.
If you have latitude and longitude variables in your data dictionary for each household you can configure the case listing screen to display the households on a map on Android. To do this, open the Mapping Options dialog from the Options menu in CSPro designer. Check the box "Show the case listing on a map" and choose the latitude and longitude variables from your dictionary. When you run the application on Android, the case listing screen will show a map with a marker for each household. Tapping on a marker launches data entry for that household.
Group Exercise
Build a CSPro application for the household listing questionnaire. Place your application in the folder Popstan2020/Listing. Treat each household as a separate case. In other words, do not use a repeating record and roster to try to match the paper questionnaire. When creating the listing dictionary use "LI_" as a prefix for the all the variable names so that they will not conflict with the names in the household dictionary. There is no need to validate the id-items using a lookup file like we did for the household questionnaire. We will prefill the id-items from the menu program tomorrow. For each household, automatically record the GPS coordinates in the latitude field. Give the interviewer the option of taking a photo of the household but allow for refusal. Name the photo based on the case id-items and put the photos in the directory Popstan2020/Data/ListingPhotos. Use the Mapping Options dialog to show the case listing on a map for the listing program. Go outside and test your listing application on a tablet.
With the
view() function you can display text files on your device. We can use this to show text documents that we write out from CSPro. To write out files from a CSEntry application we have to first declare a variable of type file :
Then we can use the commands
setfile(),
filewrite() and
close() to open, write to and close the file. Let's create a userbar button to write a simple text report that lists the household members:
// Write out and display household summary report
function showHouseholdReport()
string reportFilename = maketext("%sreport.txt", pathname(Application));
File tempFile;
setfile(tempFile, reportFilename, create);
filewrite(tempFile, "Household Summary Report");
filewrite(tempFile, "------------------------");
filewrite(tempFile, "");
filewrite(tempFile, "Province %d District %d EA %d Area type %d Household Number %d",
visualvalue(PROVINCE),
visualvalue(DISTRICT),
visualvalue(ENUMERATION_AREA),
visualvalue(AREA_TYPE),
visualvalue(HOUSEHOLD_NUMBER));
filewrite(tempFile, "");
filewrite(tempFile, "Household Members:");
filewrite(tempFile, "");
filewrite(tempFile, "Name Sex Age Relationship");
filewrite(tempFile, "---- --- --- ------------");
do numeric i = 1 while i <= totocc(HOUSEHOLD_MEMBERS_ROSTER)
filewrite(tempFile, "%s %-6s %3d %s",
NAME(i),
getlabel(SEX, visualvalue(SEX(i))),
visualvalue(AGE(i)),
getlabel(RELATIONSHIP_VS1,
visualvalue(RELATIONSHIP(i))));
enddo;
close(tempFile);
view(reportFilename);
end;
If you are familiar with HTML you can write out HTML and have nicer formatting and pictures. CSPro also has a built in templating engine to generate HTML reports using templates. See "Templated Reports" in the CSPro help for details.
- Add a button to the userbar that shows your website using the view() function.
- Add the following additional information to the summary report. a. Total number of household members, total males, total females. b. List of household assets from section G with quantity. Do not list assets where the quantity is zero.