Skip to content

Commit 33a4022

Browse files
authored
Merge pull request #669 from flynnplatt/improve_stac_geojson
initial add of STAC support
2 parents b5c065a + 7e49d21 commit 33a4022

File tree

3 files changed

+188
-14
lines changed

3 files changed

+188
-14
lines changed

src/essence/Ancillary/Description.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,34 @@ const Description = {
11181118
})
11191119
}
11201120
}
1121+
1122+
// check if this is actually a STAC feature
1123+
if (activeLayer.feature.stac_version) {
1124+
// extract links from feature assets
1125+
// https://github.com/radiantearth/stac-spec/blob/master/commons/assets.md#assets--
1126+
if (activeLayer.feature.assets) {
1127+
const assetKeys = Object.keys(
1128+
activeLayer.feature.assets
1129+
)
1130+
for (let i = 0; i < assetKeys.length; i++) {
1131+
const asset =
1132+
activeLayer.feature.assets[assetKeys[i]]
1133+
const link = asset?.href
1134+
const title = asset?.title
1135+
const roles = asset?.roles
1136+
if (
1137+
link !== null &&
1138+
link !== '' &&
1139+
(!roles || roles.indexOf('data') !== -1)
1140+
)
1141+
links.push({
1142+
name: `<span style='display: flex; justify-content: space-between;'>${title}<i class='mdi mdi-open-in-new mdi-14px' style='margin-left: 4px; margin-top: 1px;'></i></span>`,
1143+
link: link,
1144+
target: F_.cleanString(title),
1145+
})
1146+
}
1147+
}
1148+
}
11211149
}
11221150

11231151
let key = activeLayer.useKeyAsName || 'name'

src/essence/Basics/Layers_/Layers_.js

Lines changed: 154 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ const L_ = {
7878
_layersBeingMade: {},
7979
_onLoadCallbacks: [],
8080
_loaded: false,
81-
init: function (configData, missionsList, urlOnLayers) {
82-
parseConfig(configData, urlOnLayers)
81+
init: async function (configData, missionsList, urlOnLayers) {
82+
await parseConfig(configData, urlOnLayers)
8383
L_.missionsList = missionsList
8484
},
8585
onceLoaded(cb) {
@@ -2998,7 +2998,7 @@ const L_ = {
29982998
},
29992999
parseConfig: parseConfig,
30003000

3001-
resetConfig: function (data) {
3001+
resetConfig: async function (data) {
30023002
// Save so we can make sure we reproduce the same layer settings after parsing the config
30033003
const toggledArray = { ...L_.layers.on }
30043004

@@ -3010,7 +3010,7 @@ const L_ = {
30103010
L_.layers.dataFlat = []
30113011
L_._layersLoaded = []
30123012

3013-
L_.parseConfig(data)
3013+
await L_.parseConfig(data)
30143014

30153015
// Set back
30163016
L_.layers.on = { ...L_.layers.on, ...toggledArray }
@@ -3141,7 +3141,7 @@ const L_ = {
31413141
// If we have a few changes waiting in the queue, we only need to parse the config once
31423142
// as the last item in the queue should have the latest data
31433143
const lastLayer = layerQueueList[layerQueueList.length - 1]
3144-
L_.resetConfig(lastLayer.data)
3144+
await L_.resetConfig(lastLayer.data)
31453145

31463146
while (layerQueueList.length > 0) {
31473147
const firstLayer = layerQueueList.shift()
@@ -3555,7 +3555,7 @@ const L_ = {
35553555

35563556
//Takes in a configData object and does a depth-first search through its
35573557
// layers and sets L_ variables
3558-
function parseConfig(configData, urlOnLayers) {
3558+
async function parseConfig(configData, urlOnLayers) {
35593559
//Create parsed configData
35603560
L_.configData = configData
35613561

@@ -3624,11 +3624,19 @@ function parseConfig(configData, urlOnLayers) {
36243624
const layers = L_.configData.layers
36253625

36263626
//Begin recursively going through those layers
3627-
expandLayers(layers, 0, null)
3627+
await expandLayers(layers, 0, null)
3628+
3629+
async function expandLayers(d, level, prevName) {
3630+
const stacRegex = /^stac(-((item)|(catalog)|(collection)))?:/i
36283631

3629-
function expandLayers(d, level, prevName) {
36303632
//Iterate over each layer
36313633
for (let i = 0; i < d.length; i++) {
3634+
// check if this is a vector STAC catalog or collection
3635+
// if so, prefetch the data and replace this entry
3636+
if (d[i].type === 'vector' && stacRegex.test(d[i].url)) {
3637+
d[i] = await getSTACLayers(d[i])
3638+
}
3639+
36323640
// Quick hack to use uuid instead of name as main id
36333641
d[i].uuid = d[i].uuid || d[i].name
36343642
if (L_.layers.nameToUUID[d[i].name] == null)
@@ -3808,6 +3816,144 @@ function parseConfig(configData, urlOnLayers) {
38083816
//Otherwise return 0
38093817
return 0
38103818
}
3819+
3820+
// recurse through a STAC layer building sublayers
3821+
function getSTACLayers(d) {
3822+
return new Promise(async (resolve, reject) => {
3823+
let stac_data
3824+
const stacRegex =
3825+
/^(?<prefix>stac(-((item)|(catalog)|(collection)))?:)?(?<url>.*)/i
3826+
const urlMatch = d.url.match(stacRegex)
3827+
if (!urlMatch) {
3828+
console.warn('Could not process STAC URL')
3829+
resolve(d)
3830+
}
3831+
const { prefix, url } = urlMatch.groups
3832+
d.url = url // replace the current URL so we no longer need to worry about the special prefix
3833+
if (prefix !== 'stac-item:') {
3834+
$.ajax({
3835+
url: L_.getUrl('stac', d.url, d),
3836+
success: async (resp) => {
3837+
stac_data = resp
3838+
const path = d.url.split('/').slice(0, -1).join('/')
3839+
const basename = F_.fileNameFromPath(d.url)
3840+
const stac_type = stac_data.type.toLowerCase()
3841+
if (stac_type === 'catalog') {
3842+
let sublayers = []
3843+
const children = stac_data.links.filter((l) =>
3844+
/^child/i.test(l.rel)
3845+
)
3846+
const promArr = []
3847+
for (let i = 0; i < children.length; i++) {
3848+
const uuid = `${d.uuid}-${i}`
3849+
promArr.push(
3850+
getSTACLayers(
3851+
Object.assign({}, d, {
3852+
url: children[i].href.replace(
3853+
'./',
3854+
`${path}/`
3855+
),
3856+
display_name:
3857+
children[i].title ||
3858+
F_.fileNameFromPath(
3859+
children[i].href
3860+
),
3861+
uuid: uuid,
3862+
name: uuid,
3863+
})
3864+
)
3865+
)
3866+
}
3867+
3868+
try {
3869+
const subls = await Promise.all(promArr)
3870+
sublayers = sublayers.concat(subls)
3871+
} catch (err) {
3872+
console.warn(err)
3873+
resolve(d)
3874+
}
3875+
3876+
resolve(
3877+
Object.assign(
3878+
{
3879+
type: 'header',
3880+
sublayers,
3881+
description: '',
3882+
display_name: '',
3883+
name: '',
3884+
uuid: '',
3885+
},
3886+
{
3887+
description: d.description,
3888+
display_name:
3889+
d.display_name || basename,
3890+
name: d.name,
3891+
uuid: d.uuid,
3892+
}
3893+
)
3894+
)
3895+
} else if (stac_type === 'collection') {
3896+
const sublayers = []
3897+
const items = stac_data.links.filter((l) =>
3898+
/^item/i.test(l.rel)
3899+
)
3900+
for (let i = 0; i < items.length; i++) {
3901+
const uuid = `${d.uuid}-${i}`
3902+
sublayers.push(
3903+
// we shouldn't need to pre-fetch item data
3904+
Object.assign({}, d, {
3905+
url: items[i].href.replace(
3906+
'./',
3907+
`${path}/`
3908+
),
3909+
display_name:
3910+
items[i].title ||
3911+
F_.fileNameFromPath(items[i].href),
3912+
uuid: uuid,
3913+
name: uuid,
3914+
})
3915+
)
3916+
}
3917+
resolve(
3918+
Object.assign(
3919+
{
3920+
type: 'header',
3921+
sublayers,
3922+
description: '',
3923+
display_name: '',
3924+
name: '',
3925+
uuid: '',
3926+
},
3927+
{
3928+
description: d.description,
3929+
display_name:
3930+
d.display_name || basename,
3931+
name: d.name,
3932+
uuid: d.uuid,
3933+
}
3934+
)
3935+
)
3936+
} else if (/^feature(collection)?$/i.test(stac_type)) {
3937+
resolve(
3938+
Object.assign({}, d, {
3939+
display_name: d.display_name || basename,
3940+
})
3941+
)
3942+
} else {
3943+
console.warn('Could not process STAC layer')
3944+
resolve(d)
3945+
}
3946+
},
3947+
error: (resp) => {
3948+
console.warn(resp)
3949+
resolve(d)
3950+
}
3951+
})
3952+
} else {
3953+
resolve(d)
3954+
}
3955+
})
3956+
}
38113957
}
38123958

38133959
window.L_ = L_

src/essence/essence.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ var essence = {
301301
UserInterface_.updateLayerUpdateButton('DISCONNECTED')
302302
}
303303
},
304-
init: function (config, missionsList, swapping) {
304+
init: async function (config, missionsList, swapping) {
305305
//Save the config data
306306
essence.configData = config
307307

@@ -335,7 +335,7 @@ var essence = {
335335
if (!swapping) urlOnLayers = QueryURL.queryURL()
336336

337337
//Parse all the configData
338-
L_.init(essence.configData, missionsList, urlOnLayers)
338+
await L_.init(essence.configData, missionsList, urlOnLayers)
339339

340340
if (swapping) {
341341
ToolController_.clear()
@@ -440,8 +440,8 @@ var essence = {
440440
'config.json' +
441441
'?nocache=' +
442442
new Date().getTime(),
443-
function (data) {
444-
essence.makeMission(data)
443+
async function (data) {
444+
await essence.makeMission(data)
445445
}
446446
).fail(function () {
447447
console.log(
@@ -455,7 +455,7 @@ var essence = {
455455
})
456456
}
457457
},
458-
makeMission: function (data) {
458+
makeMission: async function (data) {
459459
//Remove swap tool from data.tools
460460
for (var i = data.tools.length - 1; i > 0; i--) {
461461
if (data.tools[i].name === 'Swap') {
@@ -479,7 +479,7 @@ var essence = {
479479
}
480480
}
481481

482-
essence.init(data, L_.missionsList, true)
482+
await essence.init(data, L_.missionsList, true)
483483
},
484484
fina: function () {
485485
if (!essence.finalized) {

0 commit comments

Comments
 (0)