From 6922265f6afa0022e7386bb976eeceab1822a3a0 Mon Sep 17 00:00:00 2001 From: Xiao Gui <xgui3783@gmail.com> Date: Mon, 1 Jul 2019 11:19:27 +0200 Subject: [PATCH] feat: encodding region selected as b64, greatly reduce url lengtth comparison allen v3, ~580 regions selected: - pre 2019 May release: 2854 char - post 2019 May release: 7390 char - this patch: 1997 char maintaining backwards compat --- .../atlasViewer.constantService.service.ts | 53 ++++++++++++++- .../atlasViewer.urlService.service.ts | 64 +++++++++++++++++-- 2 files changed, 112 insertions(+), 5 deletions(-) diff --git a/src/atlasViewer/atlasViewer.constantService.service.ts b/src/atlasViewer/atlasViewer.constantService.service.ts index 75a157e0c..22511be58 100644 --- a/src/atlasViewer/atlasViewer.constantService.service.ts +++ b/src/atlasViewer/atlasViewer.constantService.service.ts @@ -290,4 +290,55 @@ export const SUPPORT_LIBRARY_MAP : Map<string,HTMLElement> = new Map([ ['vue@2.5.16',parseURLToElement('https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js')], ['preact@8.4.2',parseURLToElement('https://cdn.jsdelivr.net/npm/preact@8.4.2/dist/preact.min.js')], ['d3@5.7.0',parseURLToElement('https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js')] -]) \ No newline at end of file +]) + +/** + * First attempt at encoding int (e.g. selected region, navigation location) from number (loc info density) to b64 (higher info density) + * The constraint is that the cipher needs to be commpatible with URI encoding + * and a URI compatible separator is required. + * + * The implementation below came from + * https://stackoverflow.com/a/6573119/6059235 + * + * While a faster solution exist in the same post, this operation is expected to be done: + * - once per 1 sec frequency + * - on < 1000 numbers + * + * So performance is not really that important (Also, need to learn bitwise operation) + */ + +const cipher = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-" +export const separator = "." + +export const encodeNumber = (number: number) => { + + if (isNaN(Number(number)) || number === null || + number === Number.POSITIVE_INFINITY) + throw "The input is not valid" + if (number < 0) + throw "Can't represent negative numbers now" + + let rixit // like 'digit', only in some non-decimal radix + let residual = Math.floor(number) + let result = '' + while (true) { + rixit = residual % 64 + // console.log("rixit : " + rixit) + // console.log("result before : " + result) + result = cipher.charAt(rixit) + result + // console.log("result after : " + result) + // console.log("residual before : " + residual) + residual = Math.floor(residual / 64) + // console.log("residual after : " + residual) + + if (residual == 0) + break; + } + return result +} + +export const decodeToNumber = (encodedString: string) => { + return [...encodedString].reduce((acc,curr) => { + return acc * 64 + cipher.indexOf(curr) + }, 0) +} \ No newline at end of file diff --git a/src/atlasViewer/atlasViewer.urlService.service.ts b/src/atlasViewer/atlasViewer.urlService.service.ts index b548c96f2..db47cabb7 100644 --- a/src/atlasViewer/atlasViewer.urlService.service.ts +++ b/src/atlasViewer/atlasViewer.urlService.service.ts @@ -1,11 +1,11 @@ import { Injectable } from "@angular/core"; import { Store, select } from "@ngrx/store"; -import { ViewerStateInterface, isDefined, NEWVIEWER, CHANGE_NAVIGATION, ADD_NG_LAYER, generateLabelIndexId } from "../services/stateStore.service"; +import { ViewerStateInterface, isDefined, NEWVIEWER, CHANGE_NAVIGATION, ADD_NG_LAYER } from "../services/stateStore.service"; import { PluginInitManifestInterface } from 'src/services/state/pluginState.store' import { Observable,combineLatest } from "rxjs"; import { filter, map, scan, distinctUntilChanged, skipWhile, take } from "rxjs/operators"; import { PluginServices } from "./atlasViewer.pluginService.service"; -import { AtlasViewerConstantsServices } from "./atlasViewer.constantService.service"; +import { AtlasViewerConstantsServices, encodeNumber, separator, decodeToNumber } from "./atlasViewer.constantService.service"; import { ToastService } from "src/services/toastService.service"; import { SELECT_REGIONS_WITH_ID } from "src/services/state/viewerState.store"; @@ -157,6 +157,34 @@ export class AtlasViewerURLService{ selectRegionIds: ids }) } + + const cRegionsSelectedParam = searchparams.get('cRegionsSelected') + if (cRegionsSelectedParam) { + try { + const json = JSON.parse(cRegionsSelectedParam) + + const selectRegionIds = [] + + for (let ngId in json) { + const val = json[ngId] + const labelIndicies = val.split(separator).map(decodeToNumber) + for (let labelIndex of labelIndicies) { + selectRegionIds.push(`${ngId}#${labelIndex}`) + } + } + + this.store.dispatch({ + type: SELECT_REGIONS_WITH_ID, + selectRegionIds + }) + + } catch (e) { + /** + * parsing cRegionSelected error + */ + console.log('parsing cRegionSelected error', e) + } + } } /* now that the parcellation is loaded, load the navigation state */ @@ -222,9 +250,37 @@ export class AtlasViewerURLService{ ].join('__') } break; - case 'regionsSelected': - _[key] = state[key].map(({ ngId, labelIndex })=> generateLabelIndexId({ ngId,labelIndex })).join('_') + case 'regionsSelected': { + // _[key] = state[key].map(({ ngId, labelIndex })=> generateLabelIndexId({ ngId,labelIndex })).join('_') + const ngIdLabelIndexMap : Map<string, number[]> = state[key].reduce((acc, curr) => { + const returnMap = new Map(acc) + const { ngId, labelIndex } = curr + const existingArr = (returnMap as Map<string, number[]>).get(ngId) + if (existingArr) { + existingArr.push(labelIndex) + } else { + returnMap.set(ngId, [labelIndex]) + } + return returnMap + }, new Map()) + + if (ngIdLabelIndexMap.size === 0) { + _['cRegionsSelected'] = null + _[key] = null + break; + } + + const returnObj = {} + + for (let entry of ngIdLabelIndexMap) { + const [ ngId, labelIndicies ] = entry + returnObj[ngId] = labelIndicies.map(encodeNumber).join(separator) + } + + _['cRegionsSelected'] = JSON.stringify(returnObj) + _[key] = null break; + } case 'templateSelected': case 'parcellationSelected': _[key] = state[key].name -- GitLab