terriajs
Version:
Geospatial data visualization platform.
468 lines • 19.6 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import i18next from "i18next";
import { action, computed, makeObservable, override, runInAction } from "mobx";
import { createTransformer } from "mobx-utils";
import URI from "urijs";
import isDefined from "../../../Core/isDefined";
import { isJsonString } from "../../../Core/Json";
import loadJson from "../../../Core/loadJson";
import ReferenceMixin from "../../../ModelMixins/ReferenceMixin";
import UrlMixin from "../../../ModelMixins/UrlMixin";
import { InfoSectionTraits } from "../../../Traits/TraitsClasses/CatalogMemberTraits";
import CkanItemReferenceTraits from "../../../Traits/TraitsClasses/CkanItemReferenceTraits";
import { RectangleTraits } from "../../../Traits/TraitsClasses/MappableTraits";
import CommonStrata from "../../Definition/CommonStrata";
import CreateModel from "../../Definition/CreateModel";
import createStratumInstance from "../../Definition/createStratumInstance";
import LoadableStratum from "../../Definition/LoadableStratum";
import StratumOrder from "../../Definition/StratumOrder";
import CatalogMemberFactory from "../CatalogMemberFactory";
import WebMapServiceCatalogGroup from "../Ows/WebMapServiceCatalogGroup";
import WebMapServiceCatalogItem from "../Ows/WebMapServiceCatalogItem";
import proxyCatalogItemUrl from "../proxyCatalogItemUrl";
import { createInheritedCkanSharedTraitsStratum } from "./CkanCatalogGroup";
import CkanDefaultFormatsStratum from "./CkanDefaultFormatsStratum";
export class CkanDatasetStratum extends LoadableStratum(CkanItemReferenceTraits) {
ckanItemReference;
ckanCatalogGroup;
static stratumName = "ckanDataset";
constructor(ckanItemReference, ckanCatalogGroup) {
super();
this.ckanItemReference = ckanItemReference;
this.ckanCatalogGroup = ckanCatalogGroup;
makeObservable(this);
}
duplicateLoadableStratum(_newModel) {
return new CkanDatasetStratum(this.ckanItemReference, this.ckanCatalogGroup);
}
static async load(ckanItemReference, ckanCatalogGroup) {
if (ckanItemReference._ckanDataset === undefined) {
// If we've got a dataset and no defined resource
if (ckanItemReference.datasetId !== undefined &&
ckanItemReference.resourceId !== undefined) {
ckanItemReference._ckanDataset =
await loadCkanDataset(ckanItemReference);
ckanItemReference._ckanResource = findResourceInDataset(ckanItemReference._ckanDataset, ckanItemReference.resourceId);
ckanItemReference.setSupportedFormatFromResource(ckanItemReference._ckanResource);
}
else if (ckanItemReference.datasetId !== undefined &&
ckanItemReference.resourceId === undefined) {
ckanItemReference._ckanDataset =
await loadCkanDataset(ckanItemReference);
const matched = getSupportedFormats(ckanItemReference._ckanDataset, ckanItemReference.preparedSupportedFormats);
if (matched[0] === undefined)
return undefined;
ckanItemReference._ckanResource = matched[0].resource;
ckanItemReference._supportedFormat = matched[0].format;
}
else if (ckanItemReference.datasetId === undefined &&
ckanItemReference.resourceId !== undefined) {
ckanItemReference._ckanResource =
await loadCkanResource(ckanItemReference);
ckanItemReference._supportedFormat = isResourceInSupportedFormats(ckanItemReference._ckanResource, ckanItemReference.preparedSupportedFormats);
}
}
return new CkanDatasetStratum(ckanItemReference, ckanCatalogGroup);
}
get ckanDataset() {
return this.ckanItemReference._ckanDataset;
}
get ckanResource() {
return this.ckanItemReference._ckanResource;
}
get url() {
return getCkanItemResourceUrl(this.ckanItemReference);
}
get name() {
return getCkanItemName(this.ckanItemReference);
}
get rectangle() {
if (this.ckanDataset === undefined)
return undefined;
if (this.ckanDataset.extras !== undefined) {
const out = [];
this.ckanDataset.extras.forEach((e) => {
if (e.key === "bbox-west-long")
out[0] = parseFloat(e.value);
if (e.key === "bbox-south-lat")
out[1] = parseFloat(e.value);
if (e.key === "bbox-north-lat")
out[2] = parseFloat(e.value);
if (e.key === "bbox-east-long")
out[3] = parseFloat(e.value);
});
if (out.length === 4) {
return createStratumInstance(RectangleTraits, {
west: out[0],
south: out[1],
east: out[2],
north: out[3]
});
}
}
if (this.ckanDataset.geo_coverage !== undefined) {
const bboxString = this.ckanDataset.geo_coverage;
const parts = bboxString.split(",");
if (parts.length === 4) {
return createStratumInstance(RectangleTraits, {
west: parseInt(parts[0], 10),
south: parseInt(parts[1], 10),
east: parseInt(parts[2], 10),
north: parseInt(parts[3], 10)
});
}
}
if (isDefined(this.ckanDataset.spatial) &&
this.ckanDataset.spatial !== "") {
const gj = JSON.parse(this.ckanDataset.spatial);
if (gj.type === "Polygon" && gj.coordinates[0].length === 5) {
return createStratumInstance(RectangleTraits, {
west: gj.coordinates[0][0][0],
south: gj.coordinates[0][0][1],
east: gj.coordinates[0][2][0],
north: gj.coordinates[0][2][1]
});
}
}
return undefined;
}
get info() {
function prettifyDate(date) {
if (date.match(/^\d\d\d\d-\d\d-\d\d.*/)) {
return date.substr(0, 10);
}
else
return date;
}
const outArray = [];
if (this.ckanDataset === undefined)
return outArray;
if (this.ckanDataset.license_url !== undefined) {
outArray.push(createStratumInstance(InfoSectionTraits, {
name: i18next.t("models.ckan.licence"),
content: `[${this.ckanDataset.license_title || this.ckanDataset.license_url}](${this.ckanDataset.license_url})`
}));
}
else if (this.ckanDataset.license_title !== undefined) {
outArray.push(createStratumInstance(InfoSectionTraits, {
name: i18next.t("models.ckan.licence"),
content: this.ckanDataset.license_title
}));
}
outArray.push(createStratumInstance(InfoSectionTraits, {
name: i18next.t("models.ckan.contact_point"),
content: this.ckanDataset.contact_point
}), createStratumInstance(InfoSectionTraits, {
name: i18next.t("models.ckan.datasetDescription"),
content: this.ckanDataset.notes
}), createStratumInstance(InfoSectionTraits, {
name: i18next.t("models.ckan.author"),
content: this.ckanDataset.author
}));
if (this.ckanDataset.organization) {
outArray.push(createStratumInstance(InfoSectionTraits, {
name: i18next.t("models.ckan.datasetCustodian"),
content: this.ckanDataset.organization.description ||
this.ckanDataset.organization.title
}));
}
outArray.push(createStratumInstance(InfoSectionTraits, {
name: i18next.t("models.ckan.metadata_created"),
content: prettifyDate(this.ckanDataset.metadata_created)
}), createStratumInstance(InfoSectionTraits, {
name: i18next.t("models.ckan.metadata_modified"),
content: prettifyDate(this.ckanDataset.metadata_modified)
}), createStratumInstance(InfoSectionTraits, {
name: i18next.t("models.ckan.update_freq"),
content: this.ckanDataset.update_freq
}));
return outArray;
}
/** Set isGroup = true if this turns into WMS Group (See CkanItemReference.forceLoadReference for more info) */
get isGroup() {
if (this.ckanItemReference._supportedFormat?.definition?.type ===
WebMapServiceCatalogItem.type &&
!this.ckanItemReference.wmsLayers)
return true;
}
}
__decorate([
computed
], CkanDatasetStratum.prototype, "ckanDataset", null);
__decorate([
computed
], CkanDatasetStratum.prototype, "ckanResource", null);
__decorate([
computed
], CkanDatasetStratum.prototype, "url", null);
__decorate([
computed
], CkanDatasetStratum.prototype, "name", null);
__decorate([
computed
], CkanDatasetStratum.prototype, "rectangle", null);
__decorate([
computed
], CkanDatasetStratum.prototype, "info", null);
__decorate([
computed
], CkanDatasetStratum.prototype, "isGroup", null);
StratumOrder.addLoadStratum(CkanDatasetStratum.stratumName);
export default class CkanItemReference extends UrlMixin(ReferenceMixin(CreateModel(CkanItemReferenceTraits))) {
static type = "ckan-item";
constructor(id, terria, sourceReference, strata) {
super(id, terria, sourceReference, strata);
makeObservable(this);
this.strata.set(CkanDefaultFormatsStratum.stratumName, new CkanDefaultFormatsStratum());
}
get type() {
return CkanItemReference.type;
}
get typeName() {
return i18next.t("models.ckan.name");
}
_ckanDataset = undefined;
_ckanResource = undefined;
_ckanCatalogGroup = undefined;
_supportedFormat = undefined;
get preparedSupportedFormats() {
return this.supportedResourceFormats
? this.supportedResourceFormats.map(prepareSupportedFormat)
: [];
}
setDataset(ckanDataset) {
this._ckanDataset = ckanDataset;
}
setResource(ckanResource) {
this._ckanResource = ckanResource;
}
setCkanCatalog(ckanCatalogGroup) {
this._ckanCatalogGroup = ckanCatalogGroup;
}
setSupportedFormat(format) {
this._supportedFormat = format;
}
setSupportedFormatFromResource(resource) {
this._supportedFormat = isResourceInSupportedFormats(resource, this.preparedSupportedFormats);
}
get cacheDuration() {
if (isDefined(super.cacheDuration)) {
return super.cacheDuration;
}
return "1d";
}
// We will first attach this to the CkanItemReference
// and then we'll attach it to the target model
// I wonder if it needs to be on both?
async setCkanStrata(model) {
const stratum = await CkanDatasetStratum.load(this, this._ckanCatalogGroup);
if (stratum === undefined)
return;
runInAction(() => {
model.strata.set(CkanDatasetStratum.stratumName, stratum);
});
}
setSharedStratum(inheritedPropertiesStratum) {
// The values in this stratum should not be updated as the same object is used
// in all CkanItemReferences
this.strata.set(createInheritedCkanSharedTraitsStratum.stratumName, inheritedPropertiesStratum);
}
async forceLoadReference(_previousTarget) {
await this.setCkanStrata(this);
if (this._supportedFormat === undefined)
return undefined;
const type = (this._supportedFormat.definition ?? {}).type;
if (typeof type !== "string")
return undefined;
let model;
// Special case for WMS
// Check for `layers` before creating model
// If WMS layers have been found - create WebMapServiceCatalogItem
// If no WMS layers are found - create WebMapServiceCatalogGroup
if (type === WebMapServiceCatalogItem.type) {
// If WMS layers have been found
if (this.wmsLayers) {
model = new WebMapServiceCatalogItem(this.uniqueId, this.terria, this);
model.setTrait(CommonStrata.definition, "layers", decodeURI(this.wmsLayers));
}
// if no WMS layers are found
else {
model = new WebMapServiceCatalogGroup(this.uniqueId, this.terria, this);
}
}
else {
model = CatalogMemberFactory.create(type, this.uniqueId, this.terria, this);
}
if (model === undefined)
return;
await this.setCkanStrata(model);
model.setTrait(CommonStrata.definition, "name", this.name);
return model;
}
get wmsLayers() {
const params = new URI(getCkanItemResourceUrl(this))?.search(true);
const layersFromItemProperties = this.itemPropertiesByIds?.find((itemProps) => this.uniqueId && itemProps.ids.includes(this.uniqueId))?.itemProperties?.layers ??
this.itemPropertiesByType?.find((itemProps) => itemProps.type === WebMapServiceCatalogItem.type)?.itemProperties?.layers ??
this.itemProperties?.layers;
// Mixing ?? and || because for params we don't want to use empty string params if there are non-empty string parameters
const rawLayers = (isJsonString(layersFromItemProperties)
? layersFromItemProperties
: undefined) ??
this._ckanResource?.wms_layer ??
(params?.LAYERS || params?.layers || params?.typeName);
// Improve the robustness.
const cleanLayers = rawLayers
?.split(",")
.map((layer) => layer.trim())
.join(",");
return cleanLayers;
}
}
__decorate([
computed
], CkanItemReference.prototype, "preparedSupportedFormats", null);
__decorate([
override
], CkanItemReference.prototype, "cacheDuration", null);
__decorate([
action
], CkanItemReference.prototype, "setSharedStratum", null);
__decorate([
computed
], CkanItemReference.prototype, "wmsLayers", null);
async function loadCkanDataset(ckanItem) {
const uri = new URI(ckanItem.url)
.segment("api/3/action/package_show")
.addQuery({ id: ckanItem.datasetId });
const response = await loadJson(proxyCatalogItemUrl(ckanItem, uri.toString()));
if (response.result)
return response.result;
return undefined;
}
async function loadCkanResource(ckanItem) {
const uri = new URI(ckanItem.url)
.segment("api/3/action/resource_show")
.addQuery({ id: ckanItem.resourceId });
const response = await loadJson(proxyCatalogItemUrl(ckanItem, uri.toString()));
if (response.result)
return response.result;
return undefined;
}
function findResourceInDataset(ckanDataset, resourceId) {
if (ckanDataset === undefined)
return undefined;
for (let i = 0; i < ckanDataset.resources.length; ++i) {
if (ckanDataset.resources[i].id === resourceId) {
return ckanDataset.resources[i];
}
}
return undefined;
}
export const prepareSupportedFormat = createTransformer((format) => {
return {
id: format.id,
definition: format.definition,
maxFileSize: format.maxFileSize,
removeDuplicates: format.removeDuplicates,
onlyUseIfSoleResource: format.onlyUseIfSoleResource,
formatRegex: format.formatRegex,
urlRegex: format.urlRegex,
formatRegexParsed: format.formatRegex
? new RegExp(format.formatRegex, "i")
: undefined,
urlRegexParsed: format.urlRegex
? new RegExp(format.urlRegex, "i")
: undefined
};
});
export function getSupportedFormats(dataset, formats) {
if (!dataset)
return [];
const supported = [];
for (let i = 0; i < formats.length; ++i) {
const format = formats[i];
for (let j = 0; j < dataset.resources.length; ++j) {
const resource = dataset.resources[j];
if (resourceIsSupported(resource, format)) {
supported.push({ resource: resource, format: format });
}
}
}
return supported;
}
export function isResourceInSupportedFormats(resource, formats) {
if (resource === undefined)
return undefined;
for (let i = 0; i < formats.length; ++i) {
const format = formats[i];
if (resourceIsSupported(resource, format))
return format;
}
return undefined;
}
export function resourceIsSupported(resource, format) {
let match = false;
// Does format match (formatRegex is required)
if (!isDefined(format.formatRegexParsed))
return false;
if (format.formatRegexParsed.test(resource.format)) {
match = true;
}
// Does URL match (urlRegex is optional)
if (match &&
isDefined(format.urlRegexParsed) &&
!format.urlRegexParsed.test(resource.url)) {
match = false;
}
// Is resource.size (in bytes) greater than maxFileSize? (maxFileSize is optional)
if (match &&
isDefined(format.maxFileSize) &&
format.maxFileSize !== null &&
isDefined(resource.size) &&
resource.size !== null &&
resource.size / (1024 * 1024) > format.maxFileSize) {
match = false;
}
return match;
}
export function getCkanItemName(item) {
if (!item._ckanResource)
return;
if (item.useResourceName)
return item._ckanResource.name;
// via @steve9164
/** Switched the order [check `useCombinationNameWhereMultipleResources`
* first ] that these are checked so the default is checked last. Otherwise
* setting useCombinationNameWhereMultipleResources without setting
* useDatasetNameAndFormatWhereMultipleResources to false doesn't do
* anything */
if (item._ckanDataset) {
if (item.useCombinationNameWhereMultipleResources &&
item._ckanDataset.resources.length > 1) {
return item._ckanDataset.title + " - " + item._ckanResource.name;
}
if (item.useDatasetNameAndFormatWhereMultipleResources &&
item._ckanDataset.resources.length > 1) {
return item._ckanDataset.title + " - " + item._ckanResource.format;
}
return item._ckanDataset.title;
}
return item._ckanResource.name;
}
function getCkanItemResourceUrl(item) {
if (item._ckanResource === undefined)
return undefined;
if (item._supportedFormat !== undefined) {
if ((item._supportedFormat.definition ?? {}).type === "wms" &&
item._ckanResource.wms_api_url) {
return item._ckanResource.wms_api_url;
}
}
return item._ckanResource.url;
}
//# sourceMappingURL=CkanItemReference.js.map