diff --git a/coreplugins/cesiumion/README.md b/coreplugins/cesiumion/README.md deleted file mode 100644 index ce702826..00000000 --- a/coreplugins/cesiumion/README.md +++ /dev/null @@ -1,25 +0,0 @@ -

- -

- -The Cesium Ion WebODM add-on enables you to publish and stream even the most massive of 3D Content on the web thanks to Cesium Ion. - -With 3D Tiles, even multi-gigabyte models can be streamed to any device without having to download the entire model up front. By loading 3D Tiles into CesiumJS, you can fuse your model with other datasets, add geospatial context to place it at a real world location, or overlay additional details and analysis. - -Learn more at https://cesium.com - -## Setup - -1. Grab a token with, `assets:list, assets:read, assets:write` permissions from [Cesium Ion](https://cesium.com/ion/tokens). - -1. Under the Cesium Ion tab in WebODM set the token to the code generated in ion. - -## Testing - -1. WebODM provides open datasets which can be used as a testbench for the add-on. You will have to create an account in order to access the data. Download datasets [here](https://demo.webodm.org/dashboard/). - -1. To use the dataset click on a **task**, then in the **download assets** dropdown, select all. This should download a zip on to your machine. - -1. In your instance of WebODM, create a project, click import in the upper-right hand corner, and selet the zip we just downloaded from the demo. - -1. After the import completes and a website reload all Cesium Ion functions should be available for testing. diff --git a/coreplugins/cesiumion/ThirdParty.json b/coreplugins/cesiumion/ThirdParty.json deleted file mode 100644 index c45079f3..00000000 --- a/coreplugins/cesiumion/ThirdParty.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "name": "formik", - "url": "https://github.com/jaredpalmer/formik", - "license": "MIT" - }, - { - "name": "yup", - "url": "https://github.com/jquense/yup", - "license": "MIT" - }, - { - "name": "react-bootstrap", - "url": "https://github.com/react-bootstrap/react-bootstrap", - "license": "MIT" - }, - { - "name": "boto3", - "url": "https://github.com/boto/boto3", - "license": "Apache-2.0" - } -] diff --git a/coreplugins/cesiumion/__init__.py b/coreplugins/cesiumion/__init__.py deleted file mode 100644 index 48aad58e..00000000 --- a/coreplugins/cesiumion/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .plugin import * diff --git a/coreplugins/cesiumion/api_views.py b/coreplugins/cesiumion/api_views.py deleted file mode 100644 index b844ab75..00000000 --- a/coreplugins/cesiumion/api_views.py +++ /dev/null @@ -1,314 +0,0 @@ -import sys -import time -import logging -import requests -from os import path -from enum import Enum -from itertools import chain as iter_chain - -from app.plugins.views import TaskView -from app.plugins.worker import run_function_async -from app.plugins.data_store import GlobalDataStore -from app.plugins import signals as plugin_signals - -from worker.celery import app -from django.dispatch import receiver -from django.utils.translation import ugettext_lazy as _ -from rest_framework.fields import ChoiceField, CharField, JSONField -from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework import status -from rest_framework import serializers - -from .globals import PROJECT_NAME, ION_API_URL -from .uploader import upload_to_ion - - -pluck = lambda dic, *keys: [dic[k] if k in dic else None for k in keys] - - -### ### -# API UTILS # -### ### -def get_key_for(task_id, key): - return "task_{}_{}".format(str(task_id), key) - - -def del_asset_info(task_id, asset_type, ds=None): - if ds is None: - ds = GlobalDataStore(PROJECT_NAME) - ds.del_key(get_key_for(task_id, asset_type.value)) - - -def set_asset_info(task_id, asset_type, json, ds=None): - if ds is None: - ds = GlobalDataStore(PROJECT_NAME) - return ds.set_json(get_key_for(task_id, asset_type.value), json) - - -def get_asset_info(task_id, asset_type, default=None, ds=None): - if default is None: - default = { - "id": None, - "upload": {"progress": 0, "active": False}, - "process": {"progress": 0, "active": False}, - "error": "", - } - if ds is None: - ds = GlobalDataStore(PROJECT_NAME) - return ds.get_json(get_key_for(task_id, asset_type.value), default) - - -def is_asset_task(asset_meta): - is_error = len(asset_meta["error"]) > 0 - return asset_meta["upload"]["active"] or asset_meta["process"]["active"] or is_error - - -def get_processing_assets(task_id): - ispc = app.control.inspect() - ion_tasks = set() - active = set() - from uuid import UUID - - for wtask in iter_chain(*ispc.active().values(), *ispc.reserved().values()): - args = eval(wtask["args"]) - if len(args) < 2: - continue - ion_tasks.add((str(args[0]), AssetType[args[1]])) - - for asset_type in AssetType: - asset_info = get_asset_info(task_id, asset_type) - ion_task_id = (task_id, asset_type) - if not is_asset_task(asset_info) or ion_task_id in ion_tasks: - continue - active.add(asset_type) - - return active - - -### ### -# MODEL CONFIG # -### ### -class AssetType(str, Enum): - ORTHOPHOTO = "ORTHOPHOTO" - TERRAIN_MODEL = "TERRAIN_MODEL" - SURFACE_MODEL = "SURFACE_MODEL" - POINTCLOUD = "POINTCLOUD" - TEXTURED_MODEL = "TEXTURED_MODEL" - - -class SourceType(str, Enum): - RASTER_IMAGERY = "RASTER_IMAGERY" - RASTER_TERRAIN = "RASTER_TERRAIN" - TERRAIN_DATABASE = "TERRAIN_DATABASE" - CITYGML = "CITYGML" - KML = "KML" - CAPTURE = "3D_CAPTURE" - MODEL = "3D_MODEL" - POINTCLOUD = "POINT_CLOUD" - - -class OutputType(str, Enum): - IMAGERY = "IMAGERY" - TILES = "3DTILES" - TERRAIN = "TERRAIN" - - -ASSET_TO_FILE = { - AssetType.ORTHOPHOTO: "orthophoto.tif", - AssetType.TERRAIN_MODEL: "dtm.tif", - AssetType.SURFACE_MODEL: "dsm.tif", - AssetType.POINTCLOUD: "georeferenced_model.laz", - AssetType.TEXTURED_MODEL: "textured_model.zip", -} - -FILE_TO_ASSET = dict([reversed(i) for i in ASSET_TO_FILE.items()]) - -ASSET_TO_OUTPUT = { - AssetType.ORTHOPHOTO: OutputType.IMAGERY, - AssetType.TERRAIN_MODEL: OutputType.TERRAIN, - AssetType.SURFACE_MODEL: OutputType.TERRAIN, - AssetType.POINTCLOUD: OutputType.TILES, - AssetType.TEXTURED_MODEL: OutputType.TILES, -} - -ASSET_TO_SOURCE = { - AssetType.ORTHOPHOTO: SourceType.RASTER_IMAGERY, - AssetType.TERRAIN_MODEL: SourceType.RASTER_TERRAIN, - AssetType.SURFACE_MODEL: SourceType.RASTER_TERRAIN, - AssetType.POINTCLOUD: SourceType.POINTCLOUD, - AssetType.TEXTURED_MODEL: SourceType.CAPTURE, -} - -### ### -# RECIEVERS # -### ### -@receiver(plugin_signals.task_removed, dispatch_uid="oam_on_task_removed") -@receiver(plugin_signals.task_completed, dispatch_uid="oam_on_task_completed") -def oam_cleanup(sender, task_id, **kwargs): - # When a task is removed, simply remove clutter - # When a task is re-processed, make sure we can re-share it if we shared a task previously - for asset_type in AssetType: - del_asset_info(task_id, asset_type) - - -### ### -# API VIEWS # -### ### -class EnumField(ChoiceField): - default_error_messages = {"invalid": _("No matching enum type.")} - - def __init__(self, **kwargs): - self.enum_type = kwargs.pop("enum_type") - choices = [enum_item.value for enum_item in self.enum_type] - self.choice_set = set(choices) - super().__init__(choices, **kwargs) - - def to_internal_value(self, data): - if data in self.choice_set: - return self.enum_type[data] - self.fail("invalid") - - def to_representation(self, value): - if not value: - return None - return value.value - - -class UploadSerializer(serializers.Serializer): - token = CharField() - name = CharField() - asset_type = EnumField(enum_type=AssetType) - description = CharField(default="", required=False, allow_blank=True) - attribution = CharField(default="", required=False, allow_blank=True) - options = JSONField(default={}, required=False) - - -class UpdateIonAssets(serializers.Serializer): - token = CharField() - - -class ShareTaskView(TaskView): - def get(self, request, pk=None): - task = self.get_and_check_task(request, pk) - - assets = [] - for file_name in task.available_assets: - if file_name not in FILE_TO_ASSET: - continue - asset_type = FILE_TO_ASSET[file_name] - - asset_info = get_asset_info(task.id, asset_type) - ion_id = asset_info["id"] - is_error = len(asset_info["error"]) > 0 - is_task = is_asset_task(asset_info) - is_exported = asset_info["id"] is not None and not is_task - - assets.append( - { - "type": asset_type, - "isError": is_error, - "isTask": is_task, - "isExported": is_exported, - **asset_info, - } - ) - - return Response({"items": assets}, status=status.HTTP_200_OK) - - def post(self, request, pk=None): - task = self.get_and_check_task(request, pk) - serializer = UploadSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - - token, asset_type, name, description, attribution, options = pluck( - serializer.validated_data, - "token", - "asset_type", - "name", - "description", - "attribution", - "options", - ) - asset_path = task.get_asset_download_path(ASSET_TO_FILE[asset_type]) - - # Skip already processing tasks - if asset_type not in get_processing_assets(task.id): - if asset_type == AssetType.TEXTURED_MODEL and "position" not in options: - extent = None - if task.dsm_extent is not None: - extent = task.dsm_extent.extent - if task.dtm_extent is not None: - extent = task.dtm_extent.extent - if extent is None: - print(f"Unable to find task boundary: {task}") - else: - lng, lat = extent[0], extent[1] - # height is set to zero as model is already offset - options["position"] = [lng, lat, 0] - - del_asset_info(task.id, asset_type) - asset_info = get_asset_info(task.id, asset_type) - asset_info["upload"]["active"] = True - set_asset_info(task.id, asset_type, asset_info) - - run_function_async(upload_to_ion, - task.id, - asset_type, - token, - asset_path, - name, - description, - attribution, - options, - ) - else: - print(f"Ignore running ion task {task.id} {str(asset_type)}") - - return Response(status=status.HTTP_200_OK) - - -class RefreshIonTaskView(TaskView): - def post(self, request, pk=None): - serializer = UpdateIonAssets(data=request.data) - serializer.is_valid(raise_exception=True) - - task = self.get_and_check_task(request, pk) - token = serializer.validated_data["token"] - headers = {"Authorization": f"Bearer {token}"} - - is_updated = False - # ion cleanup check - for asset_type in AssetType: - asset_info = get_asset_info(task.id, asset_type) - ion_id = asset_info["id"] - if ion_id is None: - continue - res = requests.get(f"{ION_API_URL}/assets/{ion_id}", headers=headers) - if res.status_code != 200: - del_asset_info(task.id, asset_type) - is_updated = True - - # dead task cleanup - for asset_type in get_processing_assets(task.id): - del_asset_info(task.id, asset_type) - is_updated = True - - return Response({"updated": is_updated}, status=status.HTTP_200_OK) - - -class ClearErrorsTaskView(TaskView): - def post(self, request, pk=None): - task = self.get_and_check_task(request, pk) - for asset_type in AssetType: - asset_info = get_asset_info(task.id, asset_type) - if len(asset_info["error"]) <= 0: - continue - del_asset_info(task.id, asset_type) - - return Response({"complete": True}, status=status.HTTP_200_OK) - - - - - diff --git a/coreplugins/cesiumion/app_views.py b/coreplugins/cesiumion/app_views.py deleted file mode 100644 index 85c80e2b..00000000 --- a/coreplugins/cesiumion/app_views.py +++ /dev/null @@ -1,71 +0,0 @@ -import json -import requests - -from django import forms -from django.contrib import messages -from django.http import HttpResponse -from django.shortcuts import render -from django.contrib.auth.decorators import login_required - -from .globals import ION_API_URL - - -class TokenForm(forms.Form): - token = forms.CharField( - label="", - required=False, - max_length=1024, - widget=forms.TextInput(attrs={"placeholder": "Token"}), - ) - - -def JsonResponse(dictionary): - return HttpResponse(json.dumps(dictionary), content_type="application/json") - - -def HomeView(plugin): - @login_required - def view(request): - ds = plugin.get_user_data_store(request.user) - - # if this is a POST request we need to process the form data - if request.method == "POST": - form = TokenForm(request.POST) - if form.is_valid(): - token = form.cleaned_data["token"].strip() - if len(token) > 0: - messages.success(request, "Updated Cesium ion Token!") - else: - messages.info(request, "Reset Cesium ion Token") - ds.set_string("token", token) - - form = TokenForm(initial={"token": ds.get_string("token", default="")}) - - return render( - request, - plugin.template_path("app.html"), - {"title": "Cesium Ion", "form": form}, - ) - - return view - - -def LoadButtonView(plugin): - @login_required - def view(request): - ds = plugin.get_user_data_store(request.user) - token = ds.get_string("token") - - return render( - request, - plugin.template_path("load_buttons.js"), - { - "token": token, - "app_name": plugin.get_name(), - "api_url": plugin.public_url("").rstrip("/"), - "ion_url": ION_API_URL, - }, - content_type="text/javascript", - ) - - return view diff --git a/coreplugins/cesiumion/disabled b/coreplugins/cesiumion/disabled deleted file mode 100644 index e69de29b..00000000 diff --git a/coreplugins/cesiumion/globals.py b/coreplugins/cesiumion/globals.py deleted file mode 100644 index e6b3e5c3..00000000 --- a/coreplugins/cesiumion/globals.py +++ /dev/null @@ -1,2 +0,0 @@ -PROJECT_NAME = __name__.split(".")[-2] -ION_API_URL = "https://api.cesium.com/v1" diff --git a/coreplugins/cesiumion/manifest.json b/coreplugins/cesiumion/manifest.json deleted file mode 100644 index c7a7420b..00000000 --- a/coreplugins/cesiumion/manifest.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "Cesium Ion", - "webodmMinVersion": "0.6.0", - "description": "Upload and tile ODM assets with Cesium ion.", - "version": "1.2.1", - "author": "Cesium GS, Inc", - "email": "hello@cesium.com", - "repository": "", - "tags": ["cesium ion", "ceium"], - "homepage": "https://cesium.com", - "experimental": true, - "deprecated": false -} diff --git a/coreplugins/cesiumion/model_tools.py b/coreplugins/cesiumion/model_tools.py deleted file mode 100644 index 56f5c113..00000000 --- a/coreplugins/cesiumion/model_tools.py +++ /dev/null @@ -1,71 +0,0 @@ -from os import path, mkdir, walk, remove as removeFile -from zipfile import ZipFile, ZIP_DEFLATED -from shutil import rmtree -from tempfile import mkdtemp - - -DELETE_EXTENSIONS = (".conf", ".vec", ".spt") -OBJ_FILE_EXTENSION = ".obj" -MTL_FILE_EXTENSION = ".mtl" - - -class IonInvalidZip(Exception): - pass - - -def file_walk(directory): - for root, _, file_names in walk(directory): - for file_name in file_names: - yield path.join(root, file_name) - - -def zip_dir(zip_name, directory, destructive=False): - with ZipFile(zip_name, mode="w", compression=ZIP_DEFLATED) as zipfile: - for file_path in file_walk(directory): - relpath = path.relpath(file_path, directory) - zipfile.write(file_path, relpath) - if destructive: - removeFile(file_path) - - -def to_ion_texture_model(texture_model_path, dest_directory=None, minimize_space=True): - is_tmp = False - if dest_directory is None: - is_tmp = True - dest_directory = mkdtemp() - dest_file = path.join(dest_directory, path.basename(texture_model_path)) - try: - unzip_dir = path.join(dest_directory, "_tmp") - mkdir(unzip_dir) - with ZipFile(texture_model_path) as zipfile: - zipfile.extractall(unzip_dir) - - files_to_delete = set() - found_geo = False - for file_name in file_walk(unzip_dir): - if file_name.endswith(DELETE_EXTENSIONS): - files_to_delete.add(file_name) - elif file_name.endswith(".obj"): - if "_geo" in path.basename(file_name): - found_geo = True - else: - file_name = path.splitext(file_name)[0] - files_to_delete.add(file_name + OBJ_FILE_EXTENSION) - files_to_delete.add(file_name + MTL_FILE_EXTENSION) - - if not found_geo: - raise IonInvalidZip("Unable to find geo file") - - for file_name in files_to_delete: - if not path.isfile(file_name): - continue - removeFile(file_name) - - zip_dir(dest_file, unzip_dir, destructive=minimize_space) - rmtree(unzip_dir) - except Exception as e: - if is_tmp: - rmtree(dest_directory) - raise e - - return dest_file, dest_directory diff --git a/coreplugins/cesiumion/plugin.py b/coreplugins/cesiumion/plugin.py deleted file mode 100644 index 9090d161..00000000 --- a/coreplugins/cesiumion/plugin.py +++ /dev/null @@ -1,39 +0,0 @@ -import re -import json - -from app.plugins import PluginBase, Menu, MountPoint, logger - -from .globals import PROJECT_NAME -from .api_views import ShareTaskView, RefreshIonTaskView, ClearErrorsTaskView -from .app_views import HomeView, LoadButtonView - - -class Plugin(PluginBase): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.name = PROJECT_NAME - - def main_menu(self): - return [Menu("Cesium Ion", self.public_url(""), "fa-cesium fa fa-fw")] - - def include_js_files(self): - return ["load_buttons.js"] - - def include_css_files(self): - return ["font.css", "build/TaskView.css"] - - def build_jsx_components(self): - return ["TaskView.jsx"] - - def api_mount_points(self): - return [ - MountPoint("task/(?P[^/.]+)/share", ShareTaskView.as_view()), - MountPoint("task/(?P[^/.]+)/refresh", RefreshIonTaskView.as_view()), - MountPoint("task/(?P[^/.]+)/clear", ClearErrorsTaskView.as_view()), - ] - - def app_mount_points(self): - return [ - MountPoint("$", HomeView(self)), - MountPoint("load_buttons.js$", LoadButtonView(self)), - ] diff --git a/coreplugins/cesiumion/public/TaskView.jsx b/coreplugins/cesiumion/public/TaskView.jsx deleted file mode 100644 index c518c905..00000000 --- a/coreplugins/cesiumion/public/TaskView.jsx +++ /dev/null @@ -1,268 +0,0 @@ -import React, { Component, Fragment } from "react"; - -import ErrorMessage from "webodm/components/ErrorMessage"; -import { Button } from "react-bootstrap"; - -import IonAssetButton from "./components/IonAssetButton"; -import UploadDialog from "./components/UploadDialog"; -import TasksDialog from "./components/TasksDialog"; -import AppContext from "./components/AppContext"; -import { - ImplicitTaskFetcher as TaskFetcher, - APIFetcher -} from "./components/Fetcher"; -import { AssetStyles } from "./defaults"; -import { fetchCancelable, getCookie } from "./utils"; - -export default class TaskView extends Component { - state = { - error: "", - currentAsset: null, - isTasksDialog: false, - isUploadDialogLoading: false - }; - - cancelableFetch = null; - timeoutHandler = null; - refreshAssets = null; - - onOpenUploadDialog = asset => this.setState({ currentAsset: asset }); - - onHideUploadDialog = () => - this.setState({ currentAsset: null, isUploadDialogLoading: false }); - - showTaskDialog = () => this.setState({ isTasksDialog: true }); - - hideTaskDialog = () => this.setState({ isTasksDialog: false }); - - onUploadAsset = data => { - const { task, token, apiURL } = this.props; - const { currentAsset } = this.state; - const payload = Object.assign({}, data); - - if (currentAsset === null) { - console.warning("Submissions on invalid asset"); - return; - } - - payload.token = token; - payload.asset_type = currentAsset; - - this.setState({ isUploadDialogLoading: true }); - this.cancelableFetch = fetchCancelable( - `/api${apiURL}/task/${task.id}/share`, - { - method: "POST", - credentials: "same-origin", - headers: { - "X-CSRFToken": getCookie("csrftoken"), - Accept: "application/json", - "Content-Type": "application/json" - }, - body: JSON.stringify(payload) - } - ) - .promise.then(this.refreshAssets) - .finally(this.onHideUploadDialog); - }; - - onClearFailedAssets = () => { - const { task, apiURL } = this.props; - - this.cancelableFetch = fetchCancelable( - `/api${apiURL}/task/${task.id}/clear`, - { - method: "POST", - credentials: "same-origin", - headers: { - "X-CSRFToken": getCookie("csrftoken") - } - } - ); - - this.cancelableFetch.promise.then(this.refreshAssets); - }; - - onAssetsRefreshed = ({ items = [] }) => { - const { isTasksDialog } = this.state; - const hasTasks = items.some(item => item.isTask); - - if (!hasTasks) this.hideTaskDialog(); - if (items.some(item => item.isTask && !item.isError)) { - const timeout = 4000 / (isTasksDialog ? 2 : 1); - this.timeoutHandler = setTimeout(this.refreshAssets, timeout); - } - }; - - onCleanStatus = ({ updated = false }) => { - if (!updated || this.refreshAssets == null) return; - this.refreshAssets(); - }; - - onErrorUploadDialog = msg => { - this.setState({ error: msg }); - this.onHideUploadDialog(); - }; - - handleAssetSelect = data => asset => { - const idMap = data.items - .filter(item => item.isExported) - .reduce((accum, item) => { - accum[item.type] = item.id; - return accum; - }, {}); - window.open(`https://cesium.com/ion/assets/${idMap[asset]}`); - }; - - componentWillUnmount() { - if (this.timeoutHandler !== null) { - clearTimeout(this.timeoutHandler); - this.timeoutHandler = null; - } - if (this.cancelableFetch !== null) { - try { - this.cancelableFetch.cancel(); - this.cancelableFetch = null; - } catch (exception) { - console.warning("Failed to clear dead requests"); - console.warning(exception); - } - } - - this.refreshAssets = null; - } - - render() { - const { task, token } = this.props; - const { - isTasksDialog, - isUploadDialogLoading, - isRefreshTask, - currentAsset - } = this.state; - const isUploadDialog = currentAsset !== null; - const assetName = isUploadDialog ? AssetStyles[currentAsset].name : ""; - - return ( - - -
- (this.refreshAssets = method)} - > - {({ isError, data = {} }) => { - // Asset Export and View Selector - const { items = [] } = data; - const available = items - .filter( - item => - (!item.isExported && !item.isTask) || - item.isError - ) - .map(item => item.type); - const exported = items - .filter(item => item.isExported) - .map(item => item.type); - - // Tasks Selector - const processing = items.filter( - item => item.isTask - ); - const isTasks = processing.length > 0; - const isErrors = processing.some( - item => item.isError - ); - - return ( - - {available.length > 0 && ( - - Tile in Cesium Ion - - )} - - {exported.length > 0 && ( - - View in Cesium Ion - - )} - {items.length <= 0 && ( - - )} - {isTasks && ( - - )} - - - ); - }} - -
- - - {({ isLoading, isError, data }) => { - const initialValues = {}; - - if (!isLoading && !isError && data.results.length > 0) { - const project = data.results[0]; - initialValues.name = `${project.name} | ${task.name} ⁠— ${assetName}`; - initialValues.description = project.description; - } - - return ( - - ); - }} - - -
- ); - } -} diff --git a/coreplugins/cesiumion/public/components/AppContext.jsx b/coreplugins/cesiumion/public/components/AppContext.jsx deleted file mode 100644 index 4971541c..00000000 --- a/coreplugins/cesiumion/public/components/AppContext.jsx +++ /dev/null @@ -1,8 +0,0 @@ -const AppContext = React.createContext({ - apiUrl: null, - ionURL: null, - token: null, - task: null -}); - -export default AppContext; diff --git a/coreplugins/cesiumion/public/components/BootstrapField.jsx b/coreplugins/cesiumion/public/components/BootstrapField.jsx deleted file mode 100644 index 4c0138ee..00000000 --- a/coreplugins/cesiumion/public/components/BootstrapField.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import { Formik, Field, getIn } from "formik"; - -import { - FormGroup, - ControlLabel, - FormControl, - Checkbox, - HelpBlock -} from "react-bootstrap"; - -const BootstrapFieldComponent = ({ - field, - form: { touched, errors }, - label, - help, - type = "", - showIcon = true, - ...props -}) => { - const isError = getIn(errors, field.name) && getIn(touched, field.name); - const errorMsg = getIn(errors, field.name); - let ControlComponent = FormControl; - - const testType = type.toLowerCase(); - if (testType === "checkbox") ControlComponent = Checkbox; - else if (testType === "textarea" || testType === "select") - props.componentClass = testType; - else props.type = type; - - return ( - - {label && {label}} - - {isError && {errorMsg}} - {help && !isError && {help}} - {isError && showIcon && } - - ); -}; - -const BootstrapField = props => ( - -); - -export { BootstrapFieldComponent }; -export default BootstrapField; diff --git a/coreplugins/cesiumion/public/components/Fetcher.jsx b/coreplugins/cesiumion/public/components/Fetcher.jsx deleted file mode 100644 index 3cd10a0f..00000000 --- a/coreplugins/cesiumion/public/components/Fetcher.jsx +++ /dev/null @@ -1,140 +0,0 @@ -import React, { PureComponent } from "react"; - -import AppContext from "./AppContext"; -import { fetchCancelable, getCookie } from "../utils"; - -export class Fetcher extends PureComponent { - static defaultProps = { - url: "", - path: "", - method: "GET", - onBindRefresh: () => {}, - onError: () => {}, - onLoad: () => {} - }; - - state = { - isLoading: true, - isError: false - }; - - cancelableFetch = null; - - fetch = () => { - const { - url, - path, - onError, - onLoad, - refresh, - children, - params, - ...options - } = this.props; - - let queryURL = `${url}/${path}`; - if (params !== undefined) { - const serializedParams = `?${Object.keys(params) - .map(key => - [key, params[key]].map(encodeURIComponent).join("=") - ) - .join("&")}`; - queryURL = queryURL.replace(/[\/\?]+$/, ""); - queryURL += serializedParams; - } - - this.cancelableFetch = fetchCancelable(queryURL, options); - return this.cancelableFetch.promise - .then(res => { - if (res.status !== 200) throw new Error(res.status); - return res.json(); - }) - .then(data => { - this.setState({ data, isLoading: false }); - onLoad(data); - }) - .catch(out => { - if (out.isCanceled) return; - this.setState({ error: out, isLoading: false, isError: true }); - onError(out); - }) - .finally(() => (this.cancelableFetch = null)); - }; - - componentDidMount() { - this.fetch(); - this.props.onBindRefresh(this.fetch); - } - - componentWillUnmount() { - this.props.onBindRefresh(null); - if (this.cancelableFetch === null) return; - this.cancelableFetch.cancel(); - this.cancelableFetch = null; - } - - render() { - const { children } = this.props; - if (children == null) return null; - if (typeof children !== "function") - return React.cloneElement(children, this.state); - else return children(this.state); - } -} - -const ImplicitFetcher = ({ - url, - getURL = null, - getOptions = null, - ...options -}) => ( - - {context => ( - - )} - -); - -const APIFetcher = props => ( - -); - -const ImplicitTaskFetcher = props => ( - `/api${apiURL}/task/${task.id}`} - credentials={"same-origin"} - headers={{ - "X-CSRFToken": getCookie("csrftoken"), - Accept: "application/json", - "Content-Type": "application/json" - }} - {...props} - /> -); - -const ImplicitIonFetcher = props => ( - ionURL} - getOptions={({ token }) => ({ - headers: { - Authorization: `Bearer ${token}` - } - })} - {...props} - /> -); - -export { APIFetcher, ImplicitTaskFetcher, ImplicitIonFetcher }; diff --git a/coreplugins/cesiumion/public/components/FormikErrorFocus.jsx b/coreplugins/cesiumion/public/components/FormikErrorFocus.jsx deleted file mode 100644 index 2a57cd8e..00000000 --- a/coreplugins/cesiumion/public/components/FormikErrorFocus.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from "react"; -import { connect } from "formik"; - -class FormikErrorFocus extends React.Component { - isObject(value) { - return ( - value && typeof value === "object" && value.constructor === Object - ); - } - getKeysRecursively = object => { - if (!this.isObject(object)) { - return ""; - } - const currentKey = Object.keys(object)[0]; - if (!this.getKeysRecursively(object[currentKey])) { - return currentKey; - } - return currentKey + "." + this.getKeysRecursively(object[currentKey]); - }; - componentDidUpdate(prevProps) { - const { isSubmitting, isValidating, errors } = prevProps.formik; - const keys = Object.keys(errors); - if (keys.length > 0 && isSubmitting && !isValidating) { - const selectorKey = this.getKeysRecursively(errors); - const selector = `[id="${selectorKey}"], [name="${selectorKey}"] `; - const errorElement = document.querySelector(selector); - if (errorElement) errorElement.focus(); - console.warn(errors); - } - } - render() { - return null; - } -} -export default connect(FormikErrorFocus); diff --git a/coreplugins/cesiumion/public/components/IonAssetButton.jsx b/coreplugins/cesiumion/public/components/IonAssetButton.jsx deleted file mode 100644 index 4ab4afce..00000000 --- a/coreplugins/cesiumion/public/components/IonAssetButton.jsx +++ /dev/null @@ -1,60 +0,0 @@ -import React, { PureComponent, Fragment } from "react"; - -import { DropdownButton, MenuItem } from "react-bootstrap"; - -import IonAssetLabel from "./IonAssetLabel"; -import { AssetStyles } from "../defaults"; - -import "./IonAssetButton.scss"; - -export default class IonAssetButton extends PureComponent { - static defaultProps = { - assets: [], - assetComponent: IonAssetLabel, - onSelect: () => {} - }; - - handleClick = asset => () => this.props.onSelect(asset); - - render() { - const { - assets, - onSelect, - children, - assetComponent: AssetComponent - } = this.props; - - const menuItems = assets - .sort((a, b) => - AssetStyles[a].name.localeCompare(AssetStyles[b].name) - ) - .map(asset => ( - - - - )); - - const title = ( - - - {children} - - ); - - return ( - - {menuItems} - - ); - } -} diff --git a/coreplugins/cesiumion/public/components/IonAssetButton.scss b/coreplugins/cesiumion/public/components/IonAssetButton.scss deleted file mode 100644 index 4cf08bf4..00000000 --- a/coreplugins/cesiumion/public/components/IonAssetButton.scss +++ /dev/null @@ -1,14 +0,0 @@ -.ion-btn { - .fa-cesium { - margin-right: 0.5em; - } - - .caret { - margin-left: 1em; - } -} - -.ion-dropdowns .dropdown { - float: none; - margin-right: 4px; -} diff --git a/coreplugins/cesiumion/public/components/IonAssetLabel.jsx b/coreplugins/cesiumion/public/components/IonAssetLabel.jsx deleted file mode 100644 index 18554c5a..00000000 --- a/coreplugins/cesiumion/public/components/IonAssetLabel.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import React, { PureComponent, Fragment } from "react"; -import { AssetStyles } from "../defaults"; - -const IonAssetLabel = ({ asset, showIcon = false, ...options }) => ( - - {showIcon && } - {" "} - {AssetStyles[asset].name} - -); - -export default IonAssetLabel; diff --git a/coreplugins/cesiumion/public/components/TaskDialog.scss b/coreplugins/cesiumion/public/components/TaskDialog.scss deleted file mode 100644 index b44d2095..00000000 --- a/coreplugins/cesiumion/public/components/TaskDialog.scss +++ /dev/null @@ -1,19 +0,0 @@ -.ion-tasks .list-group .list-group-item { - border-right: 0; - border-left: 0; - border-radius: 0; - padding: 20px 0; - - &:first-child { - border-top: 0; - } - - &:last-child { - margin-bottom: 0; - border-bottom: 0; - } - - .progress { - margin-bottom: 0; - } -} diff --git a/coreplugins/cesiumion/public/components/TasksDialog.jsx b/coreplugins/cesiumion/public/components/TasksDialog.jsx deleted file mode 100644 index ea7e8a1b..00000000 --- a/coreplugins/cesiumion/public/components/TasksDialog.jsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { Component, Fragment } from "react"; - -import { - Row, - Col, - Modal, - Button, - ListGroup, - ListGroupItem, - ProgressBar, - Glyphicon -} from "react-bootstrap"; - -import IonAssetLabel from "./IonAssetLabel"; -import "./TaskDialog.scss"; - -const TaskStatusItem = ({ - asset, - progress, - task, - helpText = "", - active = true, - bsStyle = "primary" -}) => ( - - - -

- -

- - -

Status: {task}

- -
- - {helpText && {helpText}} -
-); - -export default class TaskDialog extends Component { - static defaultProps = { - tasks: [], - taskComponent: TaskStatusItem - }; - - render() { - const { - tasks, - taskComponent: TaskComponent, - onClearFailed, - onHide, - ...options - } = this.props; - - let hasErrors = false; - - const taskItems = tasks.map( - ({ type: asset, upload, process, error }) => { - let task, - style, - active = true, - progress = 0; - - if (upload.active) { - progress = upload.progress; - task = "Uploading"; - style = "info"; - } else if (process.active) { - progress = process.progress; - task = "Processing"; - style = "success"; - } - - if (error.length > 0) { - task = "Error"; - style = "danger"; - active = false; - console.error(error); - hasErrors = true; - } - - return ( - - ); - } - ); - - return ( - - - - Cesium Ion Tasks - - - - {taskItems} - - {hasErrors && ( - - )} - - - - - - - ); - } -} diff --git a/coreplugins/cesiumion/public/components/UploadDialog.jsx b/coreplugins/cesiumion/public/components/UploadDialog.jsx deleted file mode 100644 index 48bcdd7f..00000000 --- a/coreplugins/cesiumion/public/components/UploadDialog.jsx +++ /dev/null @@ -1,225 +0,0 @@ -import React, { Component, Fragment } from "react"; - -import FormDialog from "webodm/components/FormDialog"; -import { Formik } from "formik"; -import * as Yup from "yup"; -import { Row, Col, Modal, Button } from "react-bootstrap"; - -import BootstrapField from "./BootstrapField"; -import FormikErrorFocus from "./FormikErrorFocus"; -import { ImplicitIonFetcher as IonFetcher } from "./Fetcher"; -import { AssetType, SourceType } from "../defaults"; -import "./UploadDialog.scss"; - -export default class UploadDialog extends Component { - static AssetSourceType = { - [AssetType.ORTHOPHOTO]: SourceType.RASTER_IMAGERY, - [AssetType.TERRAIN_MODEL]: SourceType.RASTER_TERRAIN, - [AssetType.SURFACE_MODEL]: SourceType.RASTER_TERRAIN, - [AssetType.POINTCLOUD]: SourceType.POINTCLOUD, - [AssetType.TEXTURED_MODEL]: SourceType.CAPTURE - }; - - static defaultProps = { - show: true, - asset: null, - loading: false, - initialValues: { - name: "", - description: "", - attribution: "", - options: { - baseTerrainId: "", - textureFormat: false - } - } - }; - - handleError = msg => error => { - this.props.onError(msg); - console.error(error); - }; - - onSubmit = values => { - const { asset, onSubmit } = this.props; - values = JSON.parse(JSON.stringify(values)); - const { options = {} } = values; - - switch (UploadDialog.AssetSourceType[asset]) { - case SourceType.RASTER_TERRAIN: - if (options.baseTerrainId === "") - delete options["baseTerrainId"]; - else options.baseTerrainId = parseInt(options.baseTerrainId); - options.toMeters = 1; - options.heightReference = "WGS84"; - options.waterMask = false; - break; - case SourceType.CAPTURE: - options.textureFormat = options.textureFormat ? "WEBP" : "AUTO"; - break; - } - - onSubmit(values); - }; - - getSourceFields() { - switch (UploadDialog.AssetSourceType[this.props.asset]) { - case SourceType.RASTER_TERRAIN: - const loadOptions = ({ isLoading, isError, data }) => { - if (isLoading || isError) - return ; - const userItems = data.items - .filter(item => item.type === "TERRAIN") - .map(item => ( - - )); - return [ - , - ...userItems - ]; - }; - - return ( - - - {loadOptions} - - - ); - case SourceType.CAPTURE: - return ( - - Use WebP images - - ); - default: - return null; - } - } - - getValidation() { - const schema = {}; - - switch (UploadDialog.AssetSourceType[this.props.asset]) { - case SourceType.RASTER_TERRAIN: - schema.baseTerrainId = Yup.string(); - break; - case SourceType.CAPTURE: - schema.textureFormat = Yup.boolean(); - break; - } - - return Yup.object().shape({ - name: Yup.string().required("A name is required!"), - options: Yup.object() - .shape(schema) - .default({}) - }); - } - - render() { - const { - initialValues, - onHide, - title, - loading: isLoading, - ...options - } = this.props; - - delete options["asset"]; - delete options["onSubmit"]; - - const mergedInitialValues = { - ...UploadDialog.defaultProps.initialValues, - ...initialValues - }; - - return ( - - - - {title} - - - - {({ handleSubmit = () => {} }) => ( -
- - - - - {this.getSourceFields()} - - - - - - - {isLoading && ( - - )} - {!isLoading && ( - - )} - - - )} -
-
- ); - } -} diff --git a/coreplugins/cesiumion/public/components/UploadDialog.scss b/coreplugins/cesiumion/public/components/UploadDialog.scss deleted file mode 100644 index cfc6f36c..00000000 --- a/coreplugins/cesiumion/public/components/UploadDialog.scss +++ /dev/null @@ -1,7 +0,0 @@ -.modal-backdrop { - z-index: 100000 !important; -} - -.ion-upload.modal button i { - margin-right: 1em; -} diff --git a/coreplugins/cesiumion/public/defaults/AssetStyles.jsx b/coreplugins/cesiumion/public/defaults/AssetStyles.jsx deleted file mode 100644 index 88bbc02b..00000000 --- a/coreplugins/cesiumion/public/defaults/AssetStyles.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import AssetType from "./AssetType"; - -const AssetStyles = { - [AssetType.ORTHOPHOTO]: { - name: "Orthophoto", - icon: "far fa-image" - }, - [AssetType.TERRAIN_MODEL]: { - name: "Terrain Model", - icon: "fa fa-chart-area" - }, - [AssetType.SURFACE_MODEL]: { - name: "Surface Model", - icon: "fa fa-chart-area" - }, - [AssetType.POINTCLOUD]: { - name: "Pointcloud", - icon: "fa fa-cube" - }, - [AssetType.TEXTURED_MODEL]: { - name: "Texture Model", - icon: "fab fa-connectdevelop" - } -}; - -export default AssetStyles; diff --git a/coreplugins/cesiumion/public/defaults/AssetType.jsx b/coreplugins/cesiumion/public/defaults/AssetType.jsx deleted file mode 100644 index 0d0c0fda..00000000 --- a/coreplugins/cesiumion/public/defaults/AssetType.jsx +++ /dev/null @@ -1,9 +0,0 @@ -const AssetType = { - ORTHOPHOTO: "ORTHOPHOTO", - TERRAIN_MODEL: "TERRAIN_MODEL", - SURFACE_MODEL: "SURFACE_MODEL", - POINTCLOUD: "POINTCLOUD", - TEXTURED_MODEL: "TEXTURED_MODEL" -}; - -export default AssetType; diff --git a/coreplugins/cesiumion/public/defaults/SourceType.jsx b/coreplugins/cesiumion/public/defaults/SourceType.jsx deleted file mode 100644 index 75714611..00000000 --- a/coreplugins/cesiumion/public/defaults/SourceType.jsx +++ /dev/null @@ -1,12 +0,0 @@ -const SourceType = { - RASTER_IMAGERY: "RASTER_IMAGERY", - RASTER_TERRAIN: "RASTER_TERRAIN", - TERRAIN_DATABASE: "TERRAIN_DATABASE", - CITYGML: "CITYGML", - KML: "KML", - CAPTURE: "3D_CAPTURE", - MODEL: "3D_MODEL", - POINTCLOUD: "POINT_CLOUD" -}; - -export default SourceType; diff --git a/coreplugins/cesiumion/public/defaults/index.jsx b/coreplugins/cesiumion/public/defaults/index.jsx deleted file mode 100644 index 23eac00e..00000000 --- a/coreplugins/cesiumion/public/defaults/index.jsx +++ /dev/null @@ -1,5 +0,0 @@ -import AssetType from "./AssetType"; -import SourceType from "./SourceType"; -import AssetStyles from "./AssetStyles"; - -export { AssetType, SourceType, AssetStyles }; diff --git a/coreplugins/cesiumion/public/font.css b/coreplugins/cesiumion/public/font.css deleted file mode 100644 index 8913c538..00000000 --- a/coreplugins/cesiumion/public/font.css +++ /dev/null @@ -1,25 +0,0 @@ -@font-face { - font-family: 'fa-cesium'; - src: - url('fonts/fa-cesium.ttf?rw87j5') format('truetype'), - url('fonts/fa-cesium.woff?rw87j5') format('woff'), - url('fonts/fa-cesium.svg?rw87j5#fa-cesium') format('svg'); - font-weight: normal; - font-style: normal; -} - -.fa-cesium:before { - content: "\e900"; - - font-family: 'fa-cesium' !important; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - - /* Better Font Rendering =========== */ - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} diff --git a/coreplugins/cesiumion/public/fonts/fa-cesium.svg b/coreplugins/cesiumion/public/fonts/fa-cesium.svg deleted file mode 100755 index 1cf24e5f..00000000 --- a/coreplugins/cesiumion/public/fonts/fa-cesium.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - -Generated by IcoMoon - - - - - - - \ No newline at end of file diff --git a/coreplugins/cesiumion/public/fonts/fa-cesium.ttf b/coreplugins/cesiumion/public/fonts/fa-cesium.ttf deleted file mode 100755 index 4937faff..00000000 Binary files a/coreplugins/cesiumion/public/fonts/fa-cesium.ttf and /dev/null differ diff --git a/coreplugins/cesiumion/public/fonts/fa-cesium.woff b/coreplugins/cesiumion/public/fonts/fa-cesium.woff deleted file mode 100755 index 4a898b3b..00000000 Binary files a/coreplugins/cesiumion/public/fonts/fa-cesium.woff and /dev/null differ diff --git a/coreplugins/cesiumion/public/package.json b/coreplugins/cesiumion/public/package.json deleted file mode 100644 index a944c81b..00000000 --- a/coreplugins/cesiumion/public/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "scripts": { - "dev": "webpack --watch" - }, - "dependencies": { - "formik": "^1.5.7", - "moment": "^2.24.0", - "react-bootstrap": "^0.32.4", - "yup": "^0.27.0" - } -} diff --git a/coreplugins/cesiumion/public/utils/fetchCancelable.jsx b/coreplugins/cesiumion/public/utils/fetchCancelable.jsx deleted file mode 100644 index 4d5eb1a1..00000000 --- a/coreplugins/cesiumion/public/utils/fetchCancelable.jsx +++ /dev/null @@ -1,24 +0,0 @@ -const makeCancelable = promise => { - let hasCanceled_ = false; - - const wrappedPromise = new Promise((resolve, reject) => { - promise.then( - val => (hasCanceled_ ? reject({ isCanceled: true }) : resolve(val)), - error => - hasCanceled_ ? reject({ isCanceled: true }) : reject(error) - ); - }); - - return { - promise: wrappedPromise, - cancel() { - hasCanceled_ = true; - } - }; -}; - -export { makeCancelable }; - -const fetchCancelable = (...args) => makeCancelable(fetch(...args)); - -export default fetchCancelable; diff --git a/coreplugins/cesiumion/public/utils/getCookie.jsx b/coreplugins/cesiumion/public/utils/getCookie.jsx deleted file mode 100644 index 4bfe890f..00000000 --- a/coreplugins/cesiumion/public/utils/getCookie.jsx +++ /dev/null @@ -1,9 +0,0 @@ -export default function getCookie(name) { - const value = `; ${document.cookie}`; - const parts = value.split(`; ${name}=`); - if (parts.length == 2) - return parts - .pop() - .split(";") - .shift(); -} diff --git a/coreplugins/cesiumion/public/utils/index.jsx b/coreplugins/cesiumion/public/utils/index.jsx deleted file mode 100644 index 2a265f20..00000000 --- a/coreplugins/cesiumion/public/utils/index.jsx +++ /dev/null @@ -1,4 +0,0 @@ -import fetchCancelable from "./fetchCancelable"; -import getCookie from "./getCookie"; - -export { getCookie, fetchCancelable }; diff --git a/coreplugins/cesiumion/templates/app.html b/coreplugins/cesiumion/templates/app.html deleted file mode 100644 index 2d8cbaac..00000000 --- a/coreplugins/cesiumion/templates/app.html +++ /dev/null @@ -1,64 +0,0 @@ -{% extends "app/plugins/templates/base.html" %} -{% block content %} - -