Created
March 3, 2022 00:35
-
-
Save darrenwiens/2efce0f9c13860399f03361cb4de4099 to your computer and use it in GitHub Desktop.
FMV Example, Mapbox
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<title>FMV Demo</title> | |
<meta | |
name="viewport" | |
content="initial-scale=1,maximum-scale=1,user-scalable=no" | |
/> | |
<link | |
href="https://api.mapbox.com/mapbox-gl-js/v2.7.0/mapbox-gl.css" | |
rel="stylesheet" | |
/> | |
<script src="https://api.mapbox.com/mapbox-gl-js/v2.7.0/mapbox-gl.js"></script> | |
<style> | |
body { | |
margin: 0; | |
padding: 0; | |
} | |
#map { | |
position: absolute; | |
top: 0; | |
bottom: 0; | |
width: 100%; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="map"></div> | |
<script> | |
let stac_api = "<STAC_API_URL>"; // your STAC API url | |
let stac_collection_id = "<STAC_COLLECTION_NAME>"; // your STAC collection name | |
let stac_item_id = "<STAC_ITEM_ID>"; // your STAC item ID | |
let presigned_api = "<PRESIGNED_URL_PROXY_API>"; // your presigned url proxy api | |
let mapbox_api_token = "<MAPBOX_API_TOKEN>"; // your Mapbox API token | |
let item_endpoint = `${stac_api}/collections/${stac_collection_id}/items/${stac_item_id}`; | |
let sync_camera = false; // if true, camera location will always be set to the sensor location | |
let terrain_on = false; | |
let frame_geom_data; | |
let sensor_centers; | |
let frame_centers; | |
// this function returns a POST body for an api/lambda integration | |
// that will return a presigned url for the given s3 key | |
function createPresignedRequestBody(key) { | |
return { | |
method: "POST", | |
headers: { | |
Accept: "application/json", | |
"Content-Type": "application/json", | |
}, | |
body: JSON.stringify({ object_key: key }), | |
redirect: "follow", | |
}; | |
} | |
// fetch the STAC item json | |
fetch(item_endpoint) | |
.then((res) => res.json()) | |
.then((stac_item_json) => { | |
// parse properties and hrefs from item | |
let start_dt = Date.parse( | |
stac_item_json["properties"]["start_datetime"] | |
); | |
let end_dt = Date.parse(stac_item_json["properties"]["end_datetime"]); | |
let totalSeconds = (end_dt - start_dt) / 1000; | |
let video_href = stac_item_json["assets"]["video"]["href"]; | |
let frame_geom_href = | |
stac_item_json["assets"]["video:frame-geometries"]["href"]; | |
let frame_centers_href = | |
stac_item_json["assets"]["video:frame-centers"]["href"]; | |
let sensor_centers_href = | |
stac_item_json["assets"]["video:sensor-centers"]["href"]; | |
let frame_geom_data, frame_centers_data, sensor_centers_data; | |
let frame_geom_presigned, | |
frame_centers_presigned, | |
sensor_centers_presigned, | |
video_presigned; | |
// fetch presigned urls for all of the assets (video and geojson files) | |
Promise.all([ | |
fetch(presigned_api, createPresignedRequestBody(video_href)) | |
.then((resp) => resp.text()) | |
.then((presigned_url) => { | |
video_presigned = presigned_url; | |
}), | |
fetch(presigned_api, createPresignedRequestBody(frame_geom_href)) | |
.then((resp) => resp.text()) | |
.then((presigned_url) => { | |
frame_geom_presigned = presigned_url; | |
}), | |
fetch(presigned_api, createPresignedRequestBody(frame_centers_href)) | |
.then((resp) => resp.text()) | |
.then((presigned_url) => { | |
frame_centers_presigned = presigned_url; | |
}), | |
fetch( | |
presigned_api, | |
createPresignedRequestBody(sensor_centers_href) | |
) | |
.then((resp) => resp.text()) | |
.then((presigned_url) => { | |
sensor_centers_presigned = presigned_url; | |
}), | |
]).then(() => { | |
mapboxgl.accessToken = mapbox_api_token; | |
// fetch the geojson files | |
Promise.all([ | |
fetch(frame_geom_presigned) | |
.then((resp) => resp.json()) | |
.then((asset_json) => { | |
frame_geom_data = asset_json; | |
}), | |
fetch(frame_centers_presigned) | |
.then((resp) => resp.json()) | |
.then((asset_json) => { | |
frame_centers_data = asset_json; | |
}), | |
fetch(sensor_centers_presigned) | |
.then((resp) => resp.json()) | |
.then((asset_json) => { | |
sensor_centers_data = asset_json; | |
}), | |
]).then(() => { | |
// create video style | |
const videoStyle = { | |
version: 8, | |
sources: { | |
satellite: { | |
type: "raster", | |
url: "mapbox://mapbox.satellite", | |
tileSize: 256, | |
}, | |
video: { | |
type: "video", | |
urls: [video_presigned], | |
coordinates: frame_geom_data["features"][0]["geometry"][ | |
"coordinates" | |
][0].slice(0, 4), | |
}, | |
}, | |
layers: [ | |
{ | |
id: "background", | |
type: "background", | |
paint: { | |
"background-color": "rgb(4,7,14)", | |
}, | |
}, | |
{ | |
id: "satellite", | |
type: "raster", | |
source: "satellite", | |
}, | |
{ | |
id: "video", | |
type: "raster", | |
source: "video", | |
}, | |
], | |
}; | |
// create map, including video style | |
const map = new mapboxgl.Map({ | |
container: "map", | |
zoom: 16, | |
center: | |
frame_centers_data["features"][0]["geometry"]["coordinates"], | |
bearing: 90, | |
style: videoStyle, | |
}); | |
map.addControl( | |
new mapboxgl.NavigationControl({ | |
visualizePitch: true, | |
showZoom: false, | |
}) | |
); | |
let totalFrames = frame_geom_data["features"].length; | |
const camera = map.getFreeCameraOptions(); | |
// refresh the video corner coordinates, and optionally, camera location | |
function sourceCallback() { | |
let timer_created = false; | |
if (map.getSource("video") && map.isSourceLoaded("video")) { | |
if (!timer_created) { | |
setInterval(function () { | |
updateVideoCoords(); | |
}, 30); | |
function updateVideoCoords() { | |
let curSeconds = map.getSource("video").video.currentTime; | |
let curFrameIndex = parseInt( | |
(curSeconds / totalSeconds) * totalFrames | |
); | |
let curFrameGeometry = | |
frame_geom_data["features"][curFrameIndex]; | |
map | |
.getSource("video") | |
.setCoordinates( | |
curFrameGeometry["geometry"]["coordinates"][0].slice( | |
0, | |
4 | |
) | |
); | |
if (sync_camera) { | |
let curSensorGeometry = | |
sensor_centers_data["features"][curFrameIndex][ | |
"geometry" | |
]["coordinates"]; | |
let curFrameCenter = | |
frame_centers_data["features"][curFrameIndex][ | |
"geometry" | |
]["coordinates"]; | |
zoom = 0.5; | |
let zoomed_alt; | |
if (terrain_on) { | |
zoomed_alt = | |
zoom * (curSensorGeometry[2] - curFrameCenter[2]) + | |
curFrameCenter[2]; | |
} else { | |
zoomed_alt = | |
zoom * | |
5 * | |
(curSensorGeometry[2] - curFrameCenter[2]); | |
} | |
camera.position = | |
mapboxgl.MercatorCoordinate.fromLngLat( | |
{ | |
lng: | |
zoom * | |
(curSensorGeometry[0] - curFrameCenter[0]) + | |
curFrameCenter[0], | |
lat: | |
zoom * | |
(curSensorGeometry[1] - curFrameCenter[1]) + | |
curFrameCenter[1], | |
}, | |
zoomed_alt | |
); | |
camera.lookAtPoint({ | |
lng: curFrameCenter[0], | |
lat: curFrameCenter[1], | |
}); | |
map.setFreeCameraOptions(camera); | |
} | |
} | |
} | |
timer_created = true; | |
} | |
} | |
// start refreshing the video | |
map.on("sourcedata", sourceCallback); | |
}); | |
}); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment