@@ -273,6 +273,7 @@ func newCrawler(resource string, visitor Visitor, options ...*Options) (*crawler
273
273
const (
274
274
resourceTask = "resource"
275
275
collectionsTask = "collections"
276
+ childrenTask = "children"
276
277
featuresTask = "features"
277
278
)
278
279
@@ -290,6 +291,8 @@ func (c *crawler) crawl(worker *workgroup.Worker[*Task], t *Task) error {
290
291
return c .crawlResource (worker , t .Url )
291
292
case collectionsTask :
292
293
return c .crawlCollections (worker , t .Url )
294
+ case childrenTask :
295
+ return c .crawlChildren (worker , t .Url )
293
296
case featuresTask :
294
297
return c .crawlFeatures (worker , t .Url )
295
298
default :
@@ -330,6 +333,18 @@ func (c *crawler) crawlResource(worker *workgroup.Worker[*Task], resourceUrl str
330
333
}
331
334
}
332
335
336
+ // check if this looks like a STAC API root catalog that implements STAC API - Children
337
+ if resource .Type () == Catalog && len (resource .ConformsTo ()) > 1 {
338
+ childrenLink := links .Rel ("children" , LinkTypeApplicationJSON , LinkTypeAnyJSON , LinkTypeNone )
339
+ if childrenLink != nil {
340
+ linkLoc , err := loc .Resolve (childrenLink ["href" ])
341
+ if err != nil {
342
+ return c .errorHandler (err )
343
+ }
344
+ return worker .Add (& Task {Url : linkLoc .String (), Type : childrenTask })
345
+ }
346
+ }
347
+
333
348
if resource .Type () == Collection {
334
349
// shortcut for "items" link
335
350
itemsLink := links .Rel ("items" , LinkTypeGeoJSON , LinkTypeApplicationJSON , LinkTypeAnyJSON , LinkTypeNone )
@@ -419,6 +434,52 @@ func (c *crawler) crawlCollections(worker *workgroup.Worker[*Task], collectionsU
419
434
return nil
420
435
}
421
436
437
+ func (c * crawler ) crawlChildren (worker * workgroup.Worker [* Task ], childrenUrl string ) error {
438
+ loc , locErr := normurl .New (childrenUrl )
439
+ if locErr != nil {
440
+ return locErr
441
+ }
442
+ response := & childrenResponse {}
443
+ loadErr := load (c .entry , loc , response )
444
+ if loadErr != nil {
445
+ return c .errorHandler (loadErr )
446
+ }
447
+
448
+ for i , resource := range response .Children {
449
+ if resource .Type () != Catalog && resource .Type () != Collection {
450
+ return c .errorHandler (fmt .Errorf ("expected catalog or collection at index %d, got %s" , i , resource .Type ()))
451
+ }
452
+ links := resource .Links ()
453
+
454
+ selfLink := links .Rel ("self" , LinkTypeApplicationJSON , LinkTypeAnyJSON , LinkTypeNone )
455
+ if selfLink == nil {
456
+ return c .errorHandler (fmt .Errorf ("missing self link for %s %d in %s" , resource .Type (), i , childrenUrl ))
457
+ }
458
+ selfLinkLoc , selfLinkErr := loc .Resolve (selfLink ["href" ])
459
+ if selfLinkErr != nil {
460
+ return c .errorHandler (selfLinkErr )
461
+ }
462
+ addErr := worker .Add (& Task {Url : selfLinkLoc .String (), Type : resourceTask })
463
+ if addErr != nil {
464
+ return addErr
465
+ }
466
+ }
467
+
468
+ nextLink := response .Links .Rel ("next" , LinkTypeApplicationJSON , LinkTypeAnyJSON , LinkTypeNone )
469
+ if nextLink != nil {
470
+ linkLoc , err := loc .Resolve (nextLink ["href" ])
471
+ if err != nil {
472
+ return c .errorHandler (err )
473
+ }
474
+ addErr := worker .Add (& Task {Url : linkLoc .String (), Type : childrenTask })
475
+ if addErr != nil {
476
+ return addErr
477
+ }
478
+ }
479
+
480
+ return nil
481
+ }
482
+
422
483
func (c * crawler ) crawlFeatures (worker * workgroup.Worker [* Task ], featuresUrl string ) error {
423
484
loc , locErr := normurl .New (featuresUrl )
424
485
if locErr != nil {
0 commit comments