UNPKG

terriajs

Version:

Geospatial data visualization platform.

522 lines (475 loc) 16.3 kB
import i18next from "i18next"; import { action, computed, makeObservable, override, runInAction } from "mobx"; import URI from "urijs"; import isDefined from "../../../Core/isDefined"; import loadJson from "../../../Core/loadJson"; import runLater from "../../../Core/runLater"; import { networkRequestError } from "../../../Core/TerriaError"; import AccessControlMixin from "../../../ModelMixins/AccessControlMixin"; import CatalogMemberMixin from "../../../ModelMixins/CatalogMemberMixin"; import GroupMixin from "../../../ModelMixins/GroupMixin"; import UrlMixin from "../../../ModelMixins/UrlMixin"; import ModelReference from "../../../Traits/ModelReference"; import ArcGisPortalCatalogGroupTraits from "../../../Traits/TraitsClasses/ArcGisPortalCatalogGroupTraits"; import CommonStrata from "../../Definition/CommonStrata"; import CreateModel from "../../Definition/CreateModel"; import LoadableStratum from "../../Definition/LoadableStratum"; import { BaseModel, ModelConstructorParameters } from "../../Definition/Model"; import StratumOrder from "../../Definition/StratumOrder"; import Terria from "../../Terria"; import CatalogGroup from "../CatalogGroup"; import proxyCatalogItemUrl from "../proxyCatalogItemUrl"; import { ArcGisItem, ArcGisPortalGroup, ArcGisPortalGroupSearchResponse, ArcGisPortalSearchResponse } from "./ArcGisPortalDefinitions"; import ArcGisPortalItemReference from "./ArcGisPortalItemReference"; export class ArcGisPortalStratum extends LoadableStratum( ArcGisPortalCatalogGroupTraits ) { static stratumName = "arcgisPortal"; groups: CatalogGroup[] = []; filteredGroups: CatalogGroup[] = []; datasets: ArcGisItem[] = []; filteredDatasets: ArcGisItem[] = []; constructor( readonly _catalogGroup: ArcGisPortalCatalogGroup, readonly _arcgisResponse: ArcGisPortalSearchResponse, readonly _arcgisGroupResponse: ArcGisPortalGroupSearchResponse | undefined ) { super(); makeObservable(this); this.datasets = this.getDatasets(); this.filteredDatasets = this.getFilteredDatasets(); this.groups = this.getGroups(); this.filteredGroups = this.getFilteredGroups(); } duplicateLoadableStratum(model: BaseModel): this { return new ArcGisPortalStratum( model as ArcGisPortalCatalogGroup, this._arcgisResponse, this._arcgisGroupResponse ) as this; } static async load( catalogGroup: ArcGisPortalCatalogGroup ): Promise<ArcGisPortalStratum | undefined> { let portalGroupsServerResponse: | ArcGisPortalGroupSearchResponse | undefined = undefined; let portalItemsServerResponse: ArcGisPortalSearchResponse | undefined = undefined; // If we need to group by groups we use slightly different API's // that allow us to get the data more effectively if ( catalogGroup.groupBy === "organisationsGroups" || catalogGroup.groupBy === "usersGroups" ) { if (catalogGroup.groupBy === "organisationsGroups") { const groupSearchUri = new URI(catalogGroup.url) .segment("/sharing/rest/community/groups") .addQuery({ num: 100, f: "json" }); if (catalogGroup.groupSearchParams !== undefined) { const groupSearchParams = catalogGroup.groupSearchParams; Object.keys(groupSearchParams).forEach((key: string) => groupSearchUri.addQuery(key, groupSearchParams[key]) ); } portalGroupsServerResponse = await paginateThroughResults( groupSearchUri, catalogGroup ); if (portalGroupsServerResponse === undefined) return undefined; } else if (catalogGroup.groupBy === "usersGroups") { const groupSearchUri = new URI(catalogGroup.url) .segment(`/sharing/rest/community/self`) .addQuery({ f: "json" }); const response = await getPortalInformation( groupSearchUri, catalogGroup ); if (response === undefined) return undefined; portalGroupsServerResponse = { total: response.groups.length, results: response.groups, start: 0, num: 0, nextStart: 0 }; } if (portalGroupsServerResponse === undefined) return undefined; // Then for each group we've got access to we get the content for (let i = 0; i < portalGroupsServerResponse.results.length; ++i) { const group: ArcGisPortalGroup = portalGroupsServerResponse.results[i]; const groupItemSearchUri = new URI(catalogGroup.url) .segment(`/sharing/rest/content/groups/${group.id}/search`) .addQuery({ num: 100, f: "json" }); if (catalogGroup.searchParams) { const searchParams = catalogGroup.searchParams; Object.keys(searchParams).forEach((key: string) => groupItemSearchUri.addQuery(key, searchParams[key]) ); } const groupResponse: ArcGisPortalSearchResponse | undefined = await paginateThroughResults(groupItemSearchUri, catalogGroup); if (groupResponse === undefined) return undefined; groupResponse.results.forEach((item: ArcGisItem) => { item.groupId = group.id; }); if (i === 0) { portalItemsServerResponse = groupResponse; } else if ( portalItemsServerResponse !== undefined && groupResponse !== undefined ) { portalItemsServerResponse.results = portalItemsServerResponse.results.concat(groupResponse.results); } } } else { // If we don't need to group by Portal Groups then we'll search using // the regular endpoint const itemSearchUri = new URI(catalogGroup.url) .segment("/sharing/rest/search") .addQuery({ num: 100, f: "json" }); if (catalogGroup.searchParams !== undefined) { const searchParams = catalogGroup.searchParams; const params = Object.keys(searchParams); params.forEach((key: string) => itemSearchUri.addQuery(key, searchParams[key]) ); } portalItemsServerResponse = await paginateThroughResults( itemSearchUri, catalogGroup ); if ( catalogGroup.groupBy === "portalCategories" && portalItemsServerResponse !== undefined ) { const categories = new Map(); portalItemsServerResponse.results.forEach(function (item) { item.categories.forEach(function ( rawCategory: string, index: number ) { const category = rawCategory.trim(); if (index === 0) { item.groupId = category; } // "/Categories/Land Parcel and Property" if (!categories.has(category)) { const categoryPieces = category.split("/"); const categoryGroup = { id: category, title: categoryPieces[categoryPieces.length - 1] }; categories.set(category, categoryGroup); } }); }); portalGroupsServerResponse = { total: categories.size, results: Array.from(categories.values()), start: 0, num: 0, nextStart: 0 }; } } if (portalItemsServerResponse === undefined) return undefined; return new ArcGisPortalStratum( catalogGroup, portalItemsServerResponse, portalGroupsServerResponse ); } @computed get members(): ModelReference[] { if (this.filteredGroups.length > 0) { const groupIds: ModelReference[] = []; this.filteredGroups.forEach((g) => { if (this._catalogGroup.hideEmptyGroups && g.members.length > 0) { groupIds.push(g.uniqueId as ModelReference); } else if (!this._catalogGroup.hideEmptyGroups) { groupIds.push(g.uniqueId as ModelReference); } }); return groupIds; } // Otherwise return the id's of all the resources of all the filtered datasets return this.filteredDatasets.map((ds) => { return this._catalogGroup.uniqueId + "/" + ds.id; }, this); } private getDatasets(): ArcGisItem[] { return this._arcgisResponse.results; } private getFilteredDatasets(): ArcGisItem[] { if (this.datasets.length === 0) return []; if (this._catalogGroup.excludeMembers !== undefined) { const bl = this._catalogGroup.excludeMembers; return this.datasets.filter((ds) => bl.indexOf(ds.title) === -1); } return this.datasets; } private getGroups(): CatalogGroup[] { if (this._catalogGroup.groupBy === "none") return []; const groups: CatalogGroup[] = [ ...createUngroupedGroup(this), ...createGroupsByPortalGroups(this) ]; groups.sort(function (a, b) { if (a.nameInCatalog === undefined || b.nameInCatalog === undefined) return 0; if (a.nameInCatalog < b.nameInCatalog) { return -1; } if (a.nameInCatalog > b.nameInCatalog) { return 1; } return 0; }); return groups; } private getFilteredGroups(): CatalogGroup[] { if (this.groups.length === 0) return []; if (this._catalogGroup.excludeMembers !== undefined) { const bl = this._catalogGroup.excludeMembers; return this.groups.filter((group) => { if (group.name === undefined) return false; else return bl.indexOf(group.name) === -1; }); } return this.groups; } @action createMembersFromDatasets() { this.filteredDatasets.forEach((dataset) => { this.createMemberFromDataset(dataset); }); } @action addCatalogItemToCatalogGroup( catalogItem: any, _dataset: ArcGisItem, groupId: string ) { const group: CatalogGroup | undefined = this._catalogGroup.terria.getModelById(CatalogGroup, groupId); if (group !== undefined) { group.add(CommonStrata.definition, catalogItem); } } @action addCatalogItemByPortalGroupsToCatalogGroup( catalogItem: any, dataset: ArcGisItem ) { if (dataset.groupId === undefined) { const groupId = this._catalogGroup.uniqueId + "/ungrouped"; this.addCatalogItemToCatalogGroup(catalogItem, dataset, groupId); return; } const groupId = this._catalogGroup.uniqueId + "/" + dataset.groupId; this.addCatalogItemToCatalogGroup(catalogItem, dataset, groupId); } @action createMemberFromDataset(arcgisDataset: ArcGisItem) { if (!isDefined(arcgisDataset.id)) { return; } const id = this._catalogGroup.uniqueId; const itemId = `${id}/${arcgisDataset.id}`; let item = this._catalogGroup.terria.getModelById( ArcGisPortalItemReference, itemId ); if (item === undefined) { item = new ArcGisPortalItemReference(itemId, this._catalogGroup.terria); item.setDataset(arcgisDataset); item.setArcgisPortalCatalog(this._catalogGroup); item.setSupportedFormatFromItem(arcgisDataset); item.setArcgisStrata(item); item.terria.addModel(item); } if ( this._catalogGroup.groupBy === "organisationsGroups" || this._catalogGroup.groupBy === "usersGroups" || this._catalogGroup.groupBy === "portalCategories" ) { this.addCatalogItemByPortalGroupsToCatalogGroup(item, arcgisDataset); } if ( AccessControlMixin.isMixedInto(item) && arcgisDataset.access !== undefined ) { item.setAccessType(arcgisDataset.access); } } } StratumOrder.addLoadStratum(ArcGisPortalStratum.stratumName); export default class ArcGisPortalCatalogGroup extends UrlMixin( GroupMixin(CatalogMemberMixin(CreateModel(ArcGisPortalCatalogGroupTraits))) ) { static readonly type = "arcgis-portal-group"; constructor(...args: ModelConstructorParameters) { super(...args); makeObservable(this); } get type() { return ArcGisPortalCatalogGroup.type; } get typeName() { return i18next.t("models.arcgisPortal.nameGroup"); } @override get cacheDuration(): string { if (isDefined(super.cacheDuration)) { return super.cacheDuration; } return "0d"; } protected forceLoadMetadata(): Promise<void> { const portalStratum = this.strata.get(ArcGisPortalStratum.stratumName) as | ArcGisPortalStratum | undefined; if (!portalStratum) { return ArcGisPortalStratum.load(this).then((stratum) => { if (stratum === undefined) return; runInAction(() => { this.strata.set(ArcGisPortalStratum.stratumName, stratum); }); }); } else { return Promise.resolve(); } } protected async forceLoadMembers() { const portalStratum = this.strata.get(ArcGisPortalStratum.stratumName) as | ArcGisPortalStratum | undefined; if (portalStratum) { await runLater(() => portalStratum.createMembersFromDatasets()); } } } function createGroup(groupId: string, terria: Terria, groupName: string) { const g = new CatalogGroup(groupId, terria); g.setTrait(CommonStrata.definition, "name", groupName); terria.addModel(g); return g; } function createUngroupedGroup(arcgisPortal: ArcGisPortalStratum) { const groupId = arcgisPortal._catalogGroup.uniqueId + "/ungrouped"; let existingGroup = arcgisPortal._catalogGroup.terria.getModelById( CatalogGroup, groupId ); if (existingGroup === undefined) { existingGroup = createGroup( groupId, arcgisPortal._catalogGroup.terria, arcgisPortal._catalogGroup.ungroupedTitle ); } return [existingGroup]; } function createGroupsByPortalGroups(arcgisPortal: ArcGisPortalStratum) { if (arcgisPortal._arcgisGroupResponse === undefined) return []; const out: CatalogGroup[] = []; arcgisPortal._arcgisGroupResponse.results.forEach( (group: ArcGisPortalGroup) => { const groupId = arcgisPortal._catalogGroup.uniqueId + "/" + group.id; let existingGroup = arcgisPortal._catalogGroup.terria.getModelById( CatalogGroup, groupId ); if (existingGroup === undefined) { existingGroup = createGroup( groupId, arcgisPortal._catalogGroup.terria, group.title ); if (group.description) { existingGroup.setTrait( CommonStrata.definition, "description", group.description ); } } if ( AccessControlMixin.isMixedInto(existingGroup) && group.access !== undefined ) { existingGroup.setAccessType(group.access); } out.push(existingGroup); } ); return out; } async function paginateThroughResults( uri: any, catalogGroup: ArcGisPortalCatalogGroup ) { const arcgisPortalResponse = await getPortalInformation(uri, catalogGroup); if (arcgisPortalResponse === undefined || !arcgisPortalResponse) { throw networkRequestError({ title: i18next.t("models.arcgisPortal.errorLoadingTitle"), message: i18next.t("models.arcgisPortal.errorLoadingMessage") }); } let nextStart: number = arcgisPortalResponse.nextStart; while (nextStart !== -1) { nextStart = await getMoreResults( uri, catalogGroup, arcgisPortalResponse, nextStart ); } return arcgisPortalResponse; } async function getPortalInformation( uri: any, catalogGroup: ArcGisPortalCatalogGroup ) { try { const response = await loadJson( proxyCatalogItemUrl( catalogGroup, uri.toString(), catalogGroup.cacheDuration ) ); return response; } catch (err) { console.log(err); return undefined; } } async function getMoreResults( uri: any, catalogGroup: ArcGisPortalCatalogGroup, baseResults: ArcGisPortalSearchResponse, nextResultStart: number ) { uri.setQuery("start", nextResultStart); try { const arcgisPortalResponse = await getPortalInformation(uri, catalogGroup); if (arcgisPortalResponse === undefined) { return -1; } baseResults.results = baseResults.results.concat( arcgisPortalResponse.results ); return arcgisPortalResponse.nextStart; } catch (err) { console.log(err); return -1; } }