In Part 1, you set up three geofences based on point location and search radius. This allowed you to apply the geofences by using a simple distance criterion in the Join operator. To get ready for more general geofence shapes, in this exercise you’re going to apply¬†four-sided polygons (quadrilaterals) in addition to the circular geofences. You will still use the same Join-based approach, but the
match criterion will be different. The quadrilaterals cover roughly the same areas as the circles, so it will be interesting to see how the results of both differ.
Preparation and setup
You can simply enhance¬†the main composite¬†you built in Part 1, but if you want to preserve that application while you experiment with¬†a new one, it is easy to copy what you built into a new main composite. I recommend putting it into its own namespace. For example, assuming that the solution from Part 1 is a main composite called GeospatialLabPart1 in namespace geospatial.lab.part1 (obviously, these names are arbitrary choices):
- Right-click on the existing main composite (GeospatialLabPart1) and choose Clone…
- In the Clone dialog, edit the Namespace:, Name:, and Source file: fields to your liking. In particular, get rid of the “CopyOf…” in the automatically generated name of the new main composite. When you specify a namespace that does not yet exist, Studio automatically creates it when you click OK.
- If you created types for the fixed-location streams in Part 1, your new main composite won’t compile because those types were not copied over. Because they live in a different namespace, their unqualified names are not enough for the compiler to find them.
The remedy is another
usedirective, this time for the old namespace (for example,
Make sure you have downloaded and unzipped¬†the geofence specifications archive¬†into the project
data folder. The
threegeofences.csv file contains the following three lines:
"box1","POLYGON((-122.41411 37.78224,-122.40404 37.78224,-122.40404 37.79020,-122.41411 37.79020,-122.41411 37.78224))" "box2","POLYGON((-122.40129 37.78914,-122.39626 37.78914,-122.39626 37.79312,-122.40129 37.79312,-122.40129 37.78914))" "box3","POLYGON((-122.40424 37.78539,-122.39820 37.78539,-122.39820 37.79016,-122.40424 37.79016,-122.40424 37.78539))"
The schema is simpler than for the circular ones: two
rstrings, one for the ID and one for the WKT polygon. Also note that the WKT string must be quoted, because it contains commas that would otherwise confuse the CSV parser.
Edit the main composite
Add a Join operator that matches the vehicle¬†locations against¬†the polygon geofences. You can use the same streams as before for the point locations, but in addition to the Join you have to add a Filesource to read the polygons, and you could add a FileSink to write the results to a separate file. Alternatively,¬†feed the results into the FileSink that’s already there, so the circular and quadrilateral results are intermixed. The stream types have to match, so the results will have a distance attribute that¬†does not make much sense for a point¬†that’s already determined to be inside the polygon; simply set those distances to zero.
Step by step:
- Add a FileSource and Join operator to the graph and connect them as shown below.
- Set the schema of the output stream from the new¬†FileSource (Geofences in the figure) to two rstrings, one for ID and one for the polygon WKT. Either specify the stream schema directly or create a global type first.
type GeofenceType = rstring id, rstring geometryWKT;
While you’re at it, give the stream a descriptive name, such as Geofences, and remove the operator alias.
- In the Param¬†tab of the FileSource properties, tell it to read a CSV file with the geofence specifications (
- In the new Join operator (InsideVehicles in the figure), define a short alias for each input port. For example, V (vehicles) for port 0 and G (geofences) for port 1.
Configure the sliding windows with an eviction policy of
count(0)for V and
matchparameter using the
contains()function, with the geometry of the geofence as the first argument and the vehicle location as the second.
In the Output¬†tab, assign the ID from the geofence input stream to the geofence ID attribute; give the distance attribute the value 0.0.
- In the FileSink (Writer in the figure), change the output file name to avoid colliding with the application from Part 1.
Save, correct any errors, and launch. If you still have the job from Part 1 running, you can cancel it to reduce clutter. Make sure the FileIngest and NextBusIngest jobs are still running and healthy; relaunch them if necessary.
If you are writing a single results file, as suggested in the figure above, a snippet may look like this:
... C104,1363818343,37.7857037,-122.4095988,34,171.067,POINT (-122.4095988 37.7857037),loc1,73.3755600867536 C104,1363818343,37.7857037,-122.4095988,34,171.067,POINT (-122.4095988 37.7857037),box1,0 C109,1363818343,37.7826392,-122.4066101,30,225.128,POINT (-122.4066101 37.7826392),loc1,453.350202174866 C118,1363818343,37.787,-122.3999611,40,135.243,POINT (-122.3999611 37.787),loc3,140.456525660243 C118,1363818343,37.787,-122.3999611,40,135.243,POINT (-122.3999611 37.787),box3,0 ...
In this sequence, there are two pairs of lines (green and blue). Each pair reports the same location update (vehicle ID and time) for a fixed location (
loc3) and for the corresponding quadrilateral geofence (
box3). In the middle, however, is a line that occurs only for
loc1 but not for
box1. This means that it’s in the circular geofence but not in the quadrilateral one. This is plausible, given that the distance to the center point, 453.35 m, is over 90% of¬†the radius of the loc1 circle, while the distance from the center to the edge of the quadrilateral is less than 90% of that radius. Apparently, this location falls in one of the slivers where the circle extends beyond the quad.
The only reason this is interesting is that it gives some confidence that the two approaches, testing distance from a point and testing containment in a polygon, give results that are valid and consistent. Now it is time to go beyond the simple functions and use the operator that is tailored to this use case: Geofence. Go to Part 3.
To compare, or to cheat if you get stuck, see a possible solution.