Skip to content

Commit

Permalink
Add getter for contentsmanager and filepath
Browse files Browse the repository at this point in the history
  • Loading branch information
arjxn-py committed Jan 9, 2025
1 parent 77b6056 commit 71e656c
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 162 deletions.
4 changes: 2 additions & 2 deletions packages/base/src/dialogs/symbology/hooks/useGetProperties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { GeoJSONFeature1, IJupyterGISModel } from '@jupytergis/schema';
import { useEffect, useState } from 'react';
import { loadFile } from '../../../tools';

interface IUseGetPropertiesProps {
layerId?: string;
Expand Down Expand Up @@ -35,9 +36,8 @@ export const useGetProperties = ({
throw new Error('Source not found');
}

const data = await model.loadFile(
const data = await loadFile(
source.parameters?.path,
'GeoJSONSource'
);

if (!data) {
Expand Down
12 changes: 8 additions & 4 deletions packages/base/src/formbuilder/objectform/geojsonsource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Ajv, ValidateFunction } from 'ajv';
import * as geojson from '@jupytergis/schema/src/schema/geojson.json';

import { BaseForm, IBaseFormProps } from './baseform';
import { loadFile } from '../../tools';

/**
* The form to modify a GeoJSON source.
Expand Down Expand Up @@ -67,10 +68,13 @@ export class GeoJSONSourcePropertiesForm extends BaseForm {
let valid = false;
if (path) {
try {
const geoJSONData = await this.props.model.loadFile(
path,
'GeoJSONSource'
);
this.props.model.getContentsManager();
const geoJSONData = await loadFile({
filepath: path,
type: 'GeoJSONSource',
contentsManager: this.props.model.getContentsManager(),
filePath: this.props.model.getFilePath()
});
valid = this._validate(geoJSONData);
if (!valid) {
error = `"${path}" is not a valid GeoJSON file`;
Expand Down
31 changes: 19 additions & 12 deletions packages/base/src/mainview/mainView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import AnnotationFloater from '../annotations/components/AnnotationFloater';
import { CommandIDs } from '../constants';
import { FollowIndicator } from './FollowIndicator';
import CollaboratorPointers, { ClientPointer } from './CollaboratorPointers';
import { loadFile } from '../tools';

interface IProps {
viewModel: MainViewModel;
Expand Down Expand Up @@ -409,10 +410,12 @@ export class MainView extends React.Component<IProps, IStates> {
case 'GeoJSONSource': {
const data =
source.parameters?.data ||
(await this._model.loadFile(
source.parameters?.path,
'GeoJSONSource'
));
await loadFile({
filepath: source.parameters?.path,
type: 'GeoJSONSource',
contentsManager: this._model.getContentsManager(),
filePath: this._model.getFilePath()
});

const format = new GeoJSON({
featureProjection: this._Map.getView().getProjection()
Expand All @@ -435,10 +438,12 @@ export class MainView extends React.Component<IProps, IStates> {
case 'ShapefileSource': {
const parameters = source.parameters as IShapefileSource;

const geojson = await this._model.loadFile(
parameters.path,
'ShapefileSource'
);
const geojson = await loadFile({
filepath: parameters.path,
type: 'ShapefileSource',
contentsManager: this._model.getContentsManager(),
filePath: this._model.getFilePath()
});

const geojsonData = Array.isArray(geojson) ? geojson[0] : geojson;

Expand Down Expand Up @@ -482,10 +487,12 @@ export class MainView extends React.Component<IProps, IStates> {

const extent = [minX, minY, maxX, maxY];

const imageUrl = await this._model.loadFile(
sourceParameters.url,
'ImageSource'
);
const imageUrl = await loadFile({
filepath: sourceParameters.url,
type: 'ImageSource',
contentsManager: this._model.getContentsManager(),
filePath: this._model.getFilePath()
});

newSource = new Static({
imageExtent: extent,
Expand Down
11 changes: 7 additions & 4 deletions packages/base/src/panelview/components/filter-panel/Filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
import { debounce, getLayerTileInfo } from '../../../tools';
import { IControlPanelModel } from '../../../types';
import FilterRow from './FilterRow';
import { loadFile } from '../../../tools';

/**
* The filters panel widget.
Expand Down Expand Up @@ -209,10 +210,12 @@ const FilterComponent = (props: IFilterComponentProps) => {
break;
}
case 'GeoJSONSource': {
const data = await model?.loadFile(
source.parameters?.path,
'GeoJSONSource'
);
const data = await loadFile({
filepath: source.parameters?.path,
type: 'GeoJSONSource',
contentsManager: model.getContentsManager(),
filePath: model.getFilePath()
});
data?.features.forEach((feature: GeoJSONFeature1) => {
feature.properties &&
addFeatureValue(feature.properties, aggregatedProperties);
Expand Down
131 changes: 130 additions & 1 deletion packages/base/src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ import { VectorTile } from '@mapbox/vector-tile';
import { URLExt } from '@jupyterlab/coreutils';
import { ServerConnection } from '@jupyterlab/services';
import * as d3Color from 'd3-color';
import { PathExt } from '@jupyterlab/coreutils';
import shp from 'shpjs';

import {
IDict,
IJGISLayerBrowserRegistry,
IJGISOptions,
IRasterLayerGalleryEntry
IRasterLayerGalleryEntry,
IJGISSource
} from '@jupytergis/schema';
import { Contents } from '@jupyterlab/services';
import RASTER_LAYER_GALLERY from '../rasterlayer_gallery/raster_layer_gallery.json';
import { getGdal } from './gdal';

Expand Down Expand Up @@ -452,3 +456,128 @@ export const loadGeoTIFFWithCache = async (sourceInfo: {
sourceUrl: sourceInfo.url
};
};

/**
* Generalized file reader for different source types.
*
* @param fileInfo - Object containing the file path and source type.
* @returns A promise that resolves to the file content.
*/
export const loadFile = async (fileInfo: {
filepath: string;
type: IJGISSource['type'];
contentsManager?: Contents.IManager;
filePath?: string;
}) => {
const { filepath, type, contentsManager, filePath } = fileInfo;

if (filepath.startsWith('http://') || filepath.startsWith('https://')) {
switch (type) {
case 'ImageSource': {
return filepath; // Return the URL directly
}

case 'ShapefileSource': {
try {
const response = await fetch(`/jupytergis_core/proxy?url=${filepath}`);
const arrayBuffer = await response.arrayBuffer();
const geojson = await shp(arrayBuffer);
return geojson;
} catch (error) {
console.error('Error loading remote shapefile:', error);
throw error;
}
}

default: {
throw new Error(`Unsupported URL handling for source type: ${type}`);
}
}
}

if (!contentsManager || !filePath) {
throw new Error('ContentsManager or filePath is not initialized.');
}

const absolutePath = PathExt.resolve(PathExt.dirname(filePath), filepath);

try {
const file = await contentsManager.get(absolutePath, { content: true });

if (!file.content) {
throw new Error(`File at ${absolutePath} is empty or inaccessible.`);
}

switch (type) {
case 'GeoJSONSource': {
return typeof file.content === 'string'
? JSON.parse(file.content)
: file.content;
}

case 'ShapefileSource': {
const arrayBuffer = await stringToArrayBuffer(file.content as string);
const geojson = await shp(arrayBuffer);
return geojson;
}

case 'ImageSource': {
if (typeof file.content === 'string') {
const mimeType = getMimeType(filepath);
return `data:${mimeType};base64,${file.content}`;
} else {
throw new Error('Invalid file format for image content.');
}
}

default: {
throw new Error(`Unsupported source type: ${type}`);
}
}
} catch (error) {
console.error(`Error reading file '${filepath}':`, error);
throw error;
}
};

/**
* Determine the MIME type based on the file extension.
*
* @param filename - The name of the file.
* @returns A string representing the MIME type.
*/
export const getMimeType = (filename: string): string => {
const extension = filename.split('.').pop()?.toLowerCase();

switch (extension) {
case 'png':
return 'image/png';
case 'jpg':
case 'jpeg':
return 'image/jpeg';
case 'gif':
return 'image/gif';
case 'webp':
return 'image/webp';
case 'svg':
return 'image/svg+xml';
default:
console.warn(
`Unknown file extension: ${extension}, defaulting to 'application/octet-stream'.`
);
return 'application/octet-stream';
}
};

/**
* Helper to convert a string (base64) to ArrayBuffer.
*
* @param content - File content as a base64 string.
* @returns An ArrayBuffer.
*/
export const stringToArrayBuffer = async (content: string): Promise<ArrayBuffer> => {
const base64Response = await fetch(
`data:application/octet-stream;base64,${content}`
);
return await base64Response.arrayBuffer();
};
4 changes: 2 additions & 2 deletions packages/schema/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ export interface IJupyterGISModel extends DocumentRegistry.IModel {
value: Contents.IManager | undefined,
filePath: string
): void;
getContentsManager(): Contents.IManager | undefined;
getFilePath(): string;
getContent(): IJGISContent;
getLayers(): IJGISLayers;
getLayer(id: string): IJGISLayer | undefined;
Expand All @@ -185,8 +187,6 @@ export interface IJupyterGISModel extends DocumentRegistry.IModel {
getOptions(): IJGISOptions;
setOptions(value: IJGISOptions): void;

loadFile(filepath: string, type: SourceType): Promise<any | undefined>;

removeLayerGroup(groupName: string): void;
renameLayerGroup(groupName: string, newName: string): void;
moveItemsToGroup(items: string[], groupName: string, index?: number): void;
Expand Down
Loading

0 comments on commit 71e656c

Please sign in to comment.