diff --git a/public/reearth.yml b/public/reearth.yml index d2daeae..e4409ce 100644 --- a/public/reearth.yml +++ b/public/reearth.yml @@ -46,6 +46,13 @@ extensions: field: data_source_type type: string value: cms_integration_api + - id: cms_project_id + title: CMS Project ID + type: string + availableIf: + field: data_source_type + type: string + value: cms_integration_api - id: cms_model_id title: CMS Model ID type: string @@ -94,14 +101,18 @@ extensions: field: location_type type: string value: geojson_field - - id: infobox_fields - title: Infobox Fields (comma-separated) - type: string - ui: multiline - id: marker_appearance title: Marker Appearance type: string ui: multiline + - id: inspector + title: Inspector + fields: + - id: display_fields + title: Display Fields + description: "Specify the fields to display in the infobox, separated by commas. Left empty to show all fields." + type: string + ui: multiline - id: inspector_block type: infoboxBlock name: CMS Data Inspector Block diff --git a/src/extensions/inspector_block/inspector_block.ts b/src/extensions/inspector_block/inspector_block.ts index 8466bb5..0ed5e82 100644 --- a/src/extensions/inspector_block/inspector_block.ts +++ b/src/extensions/inspector_block/inspector_block.ts @@ -13,7 +13,8 @@ const sendProperties = () => { reearth.ui.postMessage({ action: "getProperties", payload: - reearth.layers.selectedFeature?.properties?.__original || undefined, + reearth.layers.selectedFeature?.properties?.__inspector_fields || + undefined, }); }; diff --git a/src/extensions/visualizer/main/hooks.ts b/src/extensions/visualizer/main/hooks.ts index 86115e0..9061ad4 100644 --- a/src/extensions/visualizer/main/hooks.ts +++ b/src/extensions/visualizer/main/hooks.ts @@ -9,6 +9,7 @@ type WidgetProperty = { server_api_key?: string; integration_api_base_url?: string; integration_api_key?: string; + cms_project_id?: string; cms_model_id?: string; }; appearance: { @@ -25,6 +26,12 @@ type Schema = { type: string; }[]; +// Sub-collect Asset type +type Asset = { + id: string; + url: string; +}; + type Item = { id: string; fields: Field[]; @@ -91,13 +98,67 @@ export default () => { if ( !widgetProperty.api.integration_api_base_url || !widgetProperty.api.integration_api_key || + !widgetProperty.api.cms_project_id || !widgetProperty.api.cms_model_id ) { console.warn( - "Please set the Integration API Base URL, Integration API Key, and CMS Model ID in the widget properties." + "Please set the Integration API Base URL, Integration API Key, CMS Project ID, and CMS Model ID in the widget properties." ); return; } + + // Fetch Assets with pagination + let assets: Asset[]; + try { + const baseUrl = `${widgetProperty.api.integration_api_base_url}/projects/${widgetProperty.api.cms_project_id}/assets`; + const headers = { + "Content-Type": "application/json", + Authorization: `Bearer ${widgetProperty.api.integration_api_key}`, + }; + // First request to get total count + const firstResponse = await fetch(`${baseUrl}?perPage=100&page=1`, { + method: "GET", + headers, + }); + + if (!firstResponse.ok) { + throw new Error(`HTTP error! status: ${firstResponse.status}`); + } + + const firstData = await firstResponse.json(); + const totalCount = firstData.totalCount; + const perPage = 100; + const totalPages = Math.ceil(totalCount / perPage); + + assets = firstData.items || []; + + // Fetch remaining pages if there are more + if (totalPages > 1) { + const pagePromises = []; + for (let page = 2; page <= totalPages; page++) { + pagePromises.push( + fetch(`${baseUrl}?perPage=${perPage}&page=${page}`, { + method: "GET", + headers, + }) + ); + } + + const responses = await Promise.all(pagePromises); + + for (const response of responses) { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + assets = assets.concat(data.items || []); + } + } + postMsg("setAssets", assets); + } catch (error) { + console.error("Error fetching assets:", error); + } + // Fetch Schema let schema: Schema; try { @@ -169,17 +230,31 @@ export default () => { } // append schema's name to each item + // replace asset id with asset url in each item allItems = allItems.map((item: Item) => ({ ...item, fields: [ ...item.fields.map((field: Field) => ({ ...field, name: schema.find((s) => s.key === field.key)?.name, + value: + field.type === "asset" && typeof field.value === "string" + ? assets.find((a) => a.id === field.value)?.url || + field.value + : field.type === "asset" && Array.isArray(field.value) + ? (field.value as string[]) + .map( + (assetId) => + assets.find((a) => a.id === assetId)?.url || + assetId + ) + .reverse() + : field.value, })), ], })); - console.log("items", allItems); + console.log("Debug: items", allItems); postMsg("addLayer", allItems); } catch (error) { console.error("Error fetching data:", error); diff --git a/src/extensions/visualizer/visualizer.ts b/src/extensions/visualizer/visualizer.ts index d79fb43..0e1cc0e 100644 --- a/src/extensions/visualizer/visualizer.ts +++ b/src/extensions/visualizer/visualizer.ts @@ -12,19 +12,27 @@ type VisualizationConfig = { marker_appearance?: string; }; +type InspectorConfig = { + display_fields?: string; +}; + type WidgetProperty = { // ignore tha api group visualization: VisualizationConfig; + inspector: InspectorConfig; +}; + +type ItemField = { + id: string; + key: string; + type: string; + value: unknown; + name?: string; }; type Item = { id: string; - fields: { - id: string; - key: string; - type: string; - value: unknown; - }[]; + fields: ItemField[]; }; type UIMessage = @@ -119,8 +127,10 @@ const generateGeoJSON = ( } // Convert items to GeoJSON features - const fields = config.infobox_fields - ? config.infobox_fields.split(",").map((f) => f.trim()) + const inspectorConfig = + (reearth.extension?.widget?.property as WidgetProperty)?.inspector || {}; + const displayFields = inspectorConfig.display_fields + ? inspectorConfig.display_fields.split(",").map((f) => f.trim()) : []; const features = items @@ -128,22 +138,13 @@ const generateGeoJSON = ( // Prepare properties const properties: Record = {}; - if (fields.length === 0) { - // If no infobox fields specified, include all fields - item.fields.forEach((field) => { - properties[field.key] = field.value; - }); - properties.__original = item.fields; - } else { - properties.__original = []; - fields.forEach((field) => { - const fieldObj = item.fields.find((f) => f.key === field); - if (fieldObj) { - properties[field] = fieldObj.value; - properties.__original.push(fieldObj); - } - }); - } + // Add all fields as properties + item.fields.forEach((field) => { + properties[field.key] = field.value; + }); + + // Add filtered fields for inspector + properties.__inspector_fields = filterFields(item.fields, displayFields); // Get location const coordinates = []; @@ -245,3 +246,20 @@ const generateGeoJSON = ( return geoJSON; }; + +const filterFields = ( + originalFields: ItemField[], + displayFieldKeys: string[] +) => { + if (displayFieldKeys.length === 0) { + return originalFields; + } + const filteredFields = []; + for (const key of displayFieldKeys) { + const field = originalFields.find((f) => f.key === key); + if (field) { + filteredFields.push(field); + } + } + return filteredFields; +};