# Creating viewsheds with Mapbox GL and Turf.js

I was super impressed when I saw cartographer John Nelson's tweet about an ESRI project called the Campus Blue Lights App. The ArcGIS Online application enables a user to add, remove and position Blue Lights (emergency telephones) in the context of a campus and evaluate the visibility of each beacon. The detailed write-up inspired me to try and recreate the same functionality using Mapbox GL and TurfJS. I have attempted to explain the process below, but the entire code is also available here.

The first step in creating the viewsheds was to figure out a way to create an array of points that completed a full ring around a centre point. The first attempt looked something like this:

```for (i = 0; i < 360; i++) {
var x = centerX + radius * Math.cos(2 * Math.PI * i / 360);
var y = centerY + radius * Math.sin(2 * Math.PI * i / 360);
}
```

This successfully created 360 points, but as you moved away from the equator the resulting buffer became more skewed due to the Web Mercator projection. To account for this, I needed to create geodesic lines. Luckily, I found the perfect solution on Turf's Github page by Mathieu Albrespy. His code accounts for the curvature of the Earth.

I then used the following Turf functions to convert the 360 points into LinesStrings and detect whether any of them intersected any of the building polygons.

```var matchLine = turf.lineString([[centerX, centerY], [x, y]]);

for (n = 0; n < buildings.features.length; n++) {

var polyInfo = [buildings.features[n].geometry.coordinates[0]];
var matchPoly = turf.polygon(polyInfo);

intersection = turf.lineIntersect(matchLine, matchPoly);
}
```

Turf's 'intersection' function returns a list of all intersecting points, which in this case, provided all of the points where one of the LineStrings crossed a building polygon. If an intersection was detected I then pushed the returned coordinate data to an array.

`intersectionArray.push(intersection.features);`

If there were multiple intersections I then used Turf's 'nearest' function to determine which intersection point was closest to the centre (targetPoint).

```var targetPoint = turf.point([centerX, centerY]);
var points = turf.featureCollection(pointsArray);
nearest = turf.nearestPoint(targetPoint, points);```

The nearest coordinate information was then pushed to the geojson containing the data for the viewshed polygon. If there was no intersection, the original x and y coordinates were pushed.

```if (conflictCheck == true) {
var targetPoint = turf.point([centerX, centerY]);
var points = turf.featureCollection(pointsArray);
nearest = turf.nearestPoint(targetPoint, points);
geojsonPolyArray.push([intersectCon[0], intersectCon[1]]);
} else {
geojsonPolyArray.push([x, y]);
}

```

The resulting geojsonPolyArray was then placed inside of the data source of the viewshed layer. Once the geojsonPolyArray was built I updated the source of the viewshed layer.

```function updatePoly() {
geojsonPolyArray = [];
geojsonPolyCoords = [];
geojsonPoly = {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": geojsonPolyCoords
}
};

evalPoly(); //creates linestrings, detects intersections, and creates geojsonPolyArray

map.getSource('polygon').setData(geojsonPoly);
}```

Below is the finished product. I added a bunch of cosmetic things, but I hope the code can help someone else create their own viewsheds. Eventually I'm hoping to integrate a z factor into this using Mapbox's fill-extrusion capabilities. If anyone has questions or wants to help develop a better version of this hit me up on twitter.

Tour the pyramids
Bare bones

Drag and drop the marker