Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
- To Access current (unstable) development - (Version _next_, unreleased) use docker tag: `nightly`

## Version 2021.2
- 2021.2.4 (July 2021)
- docker tag: `latest`, `release-2021.2`, `release-2021.2.4`
- 2021.2.3 (June 2021)
- docker tag: `latest`, `release-2021.2`, `release-2021.2.3`
- docker tag: `release-2021.2.3`
- 2021.2.2 (June 2021)
- docker tag: `release-2021.2.2`
- 2021.2.1 (May 2021)
Expand Down Expand Up @@ -48,6 +50,11 @@
- Fixed: further refinement to error handling when retrieving TAP error documents
- Fixed: Simbad name resolution issue ([Firefly-797](https://github.com/Caltech-IPAC/firefly/pull/1103))
- Fixed: Mouse zoom not working correctly ([Firefly-803](https://github.com/Caltech-IPAC/firefly/pull/1103))
- 2021.2.4
- Fixed: Image mask not zooming correctly ([Firefly-810](https://github.com/Caltech-IPAC/firefly/pull/1106))
- Fixed: HiPS select table _i_ link not working ([Firefly-819](https://github.com/Caltech-IPAC/firefly/pull/1106))
- Fixed: bias slider not working in 3 color mode ([PR](https://github.com/Caltech-IPAC/firefly/pull/1106))
- Fixed: boolean table filters not working ([Firefly-805](https://github.com/Caltech-IPAC/firefly/pull/1104))


##### _Pull Request in this release_
Expand All @@ -58,7 +65,7 @@

## Version 2021.1
- 2021.1.0 (February 2021)
- docker tag: `latest`, `release-2021.1`, `release-2021.1.0`
- docker tag: `release-2021.1`, `release-2021.1.0`
- original release


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ public class ServerParams {
public static final String LOGOUT = "CmdLogout";
public static final String TILE_SIZE = "tileSize";
public static final String BACK_TO_URL= "backToUrl";
public static final String MASK_DATA= "maskData";
public static final String MASK_BITS= "maskBits";

//Workspaces
public static final String WS_LIST = "wsList"; // Gets the list of content/files
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,10 @@ public static class ByteAryCmd extends ServerCommandAccess.HttpCommand {
public void processRequest(HttpServletRequest req, HttpServletResponse res, SrvParam sp) throws Exception {

PlotState state= sp.getState();
boolean mask= sp.getOptionalBoolean(ServerParams.MASK_DATA,false);
int maskBits= sp.getOptionalInt(ServerParams.MASK_BITS,0);
int tileSize = sp.getRequiredInt(ServerParams.TILE_SIZE);
byte [] byte1D= VisServerOps.getByteStretchArray(state,tileSize);
byte [] byte1D= VisServerOps.getByteStretchArray(state,tileSize,mask,maskBits);
res.setContentType("application/octet-stream");

ByteBuffer byteBuf = ByteBuffer.wrap(byte1D);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@
import edu.caltech.ipac.visualize.plot.Histogram;
import edu.caltech.ipac.visualize.plot.ImageData;
import edu.caltech.ipac.visualize.plot.ImageHeader;
import edu.caltech.ipac.visualize.plot.ImageMask;
import edu.caltech.ipac.visualize.plot.RangeValues;
import edu.caltech.ipac.visualize.plot.plotdata.FitsRead;
import edu.caltech.ipac.visualize.plot.plotdata.RGBIntensity;

import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
Expand All @@ -38,7 +42,8 @@ public class DirectStretchUtils {
return flipped;
}

public static byte [] getStretchData(PlotState state, ActiveFitsReadGroup frGroup, int tileSize) throws InterruptedException {
public static byte [] getStretchData(PlotState state, ActiveFitsReadGroup frGroup, int tileSize, boolean mask, long maskBits)
throws InterruptedException {

FitsRead fr= frGroup.getFitsRead(state.firstBand());
int totWidth= fr.getNaxis1();
Expand Down Expand Up @@ -118,6 +123,41 @@ public class DirectStretchUtils {
}
}

}
else if (mask) {
float [] float1d= fr.getRawFloatAry();
final float [] flip1d= flipFloatArray(float1d,totWidth,totHeight);
byte1d= new byte[flip1d.length];

List<ImageMask> masksList= new ArrayList<ImageMask>();
for(int j= 0; (j<31); j++) {
if (((maskBits>>j) & 1) != 0) {
masksList.add(new ImageMask(j,Color.RED));
}
}
ImageMask[] maskAry= masksList.toArray(new ImageMask[0]);


for(int i= 0; i<xPanels; i++) {
for(int j= 0; j<yPanels; j++) {
int width= (i<xPanels-1) ? tileSize : ((totWidth-1) % tileSize + 1);
int height= (j<yPanels-1) ? tileSize : ((totHeight-1) % tileSize + 1);
idx= (i*yPanels) +j;
imageDataAry[idx]= new ImageData( maskAry,
state.getRangeValues(), tileSize*i,tileSize*j, width, height);
ImageData im= imageDataAry[idx];

executor.execute(() -> im.stretchMaskAndSave(flip1d,fr.getNaxis1()));
}
}
executor.shutdown();
normalTermination= executor.awaitTermination(600, TimeUnit.SECONDS);
for (ImageData imageData : imageDataAry) {
byte[] tmpByteAry = imageData.getSavedStandardStretch();
System.arraycopy(tmpByteAry, 0, byte1d, bPos, tmpByteAry.length);
bPos += tmpByteAry.length;
}

}
else {
float [] float1d= fr.getRawFloatAry();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,12 +406,12 @@ public static float[] getFloatDataArray(PlotState state, Band band) {



public static byte[] getByteStretchArray(PlotState state, int tileSize) {
public static byte[] getByteStretchArray(PlotState state, int tileSize, boolean mask, long maskBits) {
try {
long start = System.currentTimeMillis();
ActiveCallCtx ctx = CtxControl.prepare(state);
ActiveFitsReadGroup frGroup= ctx.getFitsReadGroup();
byte [] byte1d= DirectStretchUtils.getStretchData(state,frGroup,tileSize);
byte [] byte1d= DirectStretchUtils.getStretchData(state,frGroup,tileSize,mask, maskBits);
long elapse = System.currentTimeMillis() - start;
PlotServUtils.statsLog("byteAry",
"total-MB", (float)byte1d.length / StringUtils.MEG,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ private void setupMeta(DataGroup dg, boolean bMulti) {
col.setWidth(4);
col.setFilterable(false);
col.setSortable(false);
col.setLinkInfos(Collections.singletonList(new LinkInfo(null, INFO_ICON_STUB, null, "link to HiPS properties", null, null, null)));
col.setLinkInfos(Collections.singletonList(new LinkInfo(null, INFO_ICON_STUB, "${Properties}", "link to HiPS properties", null, null, null)));
}

private static DataGroup createTableDataFromListEntry(List<HiPSMasterListEntry> hipsMaps) {
Expand Down
14 changes: 14 additions & 0 deletions src/firefly/java/edu/caltech/ipac/visualize/plot/ImageData.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,24 @@ public byte[] stretch8bit(final float [] float1d, final Header header, final His
return byteAry;
}

public byte[] stretchMask(final float [] float1d, final int naxis1) {
byte [] byteAry= new byte[this.width * this.height];
byte blank_pixel_value = (byte) 255;
int[] pixelhist = new int[256];

ImageStretch.stretchPixelsForMask(x, lastPixel, y, lastLine, naxis1,
blank_pixel_value, float1d, byteAry, pixelhist, imageMasks);
return byteAry;
}

public void stretch8bitAndSave(final float [] float1d, final Header header, final Histogram histogram) {
this.saveStandardStretch = stretch8bit(float1d,header,histogram);
}

public void stretchMaskAndSave(final float [] float1d, final int naxis1) {
this.saveStandardStretch = stretchMask(float1d,naxis1);
}

public byte[] getSavedStandardStretch() { return this.saveStandardStretch; }

public void stretch3ColorAndSave(float [][] float1dAry, ImageHeader [] imHeadAry, Histogram[] histAry,
Expand Down
2 changes: 2 additions & 0 deletions src/firefly/js/data/ServerParams.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export const ServerParams = {
SCROLL_Y: 'scrollY',
ZOOM_FACTOR: 'zoomFactor',
RANGE_VALUES: 'rangeValues',
MASK_DATA: 'maskData',
MASK_BITS: 'maskBits',

EXT_TYPE: 'extType',
IMAGE: 'image',
Expand Down
2 changes: 1 addition & 1 deletion src/firefly/js/util/Color.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function toRGBA(/* String */ color, /* Number */ alpha=1) {
}
}

function toRGB(/* String */ color) {
export function toRGB(/* String */ color) {
if (color.startsWith('rgb')) {
const intColor= toRGBA(color);
intColor.length=3;
Expand Down
2 changes: 1 addition & 1 deletion src/firefly/js/util/WebUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const isOffscreenCanvas= (b) => getGlobalObj().OffscreenCanvas && (b inst

export function createCanvas(width,height) {
const global= getGlobalObj();
const c = global.document ? global.document.createElement('canvas') : new OffscreenCanvas(w,h);
const c = global.document ? global.document.createElement('canvas') : new OffscreenCanvas(width,height);
c.width = width;
c.height = height;
return c;
Expand Down
4 changes: 2 additions & 2 deletions src/firefly/js/visualize/ImagePlotCntlr.js
Original file line number Diff line number Diff line change
Expand Up @@ -939,8 +939,8 @@ export function dispatchUseTableAutoScroll(useAutoScroll) {
flux.process({ type: USE_TABLE_AUTO_SCROLL, payload: {useAutoScroll} });
}

export function dispatchRequestLocalData({plotId, plotImageId, dataRequested=true}) {
flux.process({ type: REQUEST_LOCAL_DATA, payload: {plotId,plotImageId, dataRequested} });
export function dispatchRequestLocalData({plotId, plotImageId, imageOverlayId, dataRequested=true}) {
flux.process({ type: REQUEST_LOCAL_DATA, payload: {plotId,plotImageId, dataRequested, imageOverlayId} });
}

/**
Expand Down
26 changes: 21 additions & 5 deletions src/firefly/js/visualize/ImageViewerLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,29 @@ const rootStyle= {
overflow:'hidden'
};

function getDataIfNecessary(pv) {
function getPlotImageToRequest(pv) {
const plot= primePlot(pv);
if (!plot || plot.dataRequested) return;
if (!plot) return undefined;
const {viewDim:{width,height}}= pv;
if (!width || !height) return;
const {plotImageId,plotId}= plot;
dispatchRequestLocalData({plotId,plotImageId});
if (!width || !height) return undefined;
if (plot.dataRequested && !pv.overlayPlotViews?.length) return undefined;
if (!plot.dataRequested) return {plotImageId:plot.plotImageId};
if (pv.overlayPlotViews?.length) {
const overPv= pv.overlayPlotViews.find( (oPv) => oPv?.plot?.dataRequested===false);
if (!overPv) return undefined;
return {plotImageId:overPv.plot.plotImageId, imageOverlayId:overPv.imageOverlayId};
}
return undefined;

}

function getDataIfNecessary(pv) {
const result= getPlotImageToRequest(pv);
if (!result) return;
const plot= primePlot(pv);
if (!plot) return;
const {plotId}= plot;
dispatchRequestLocalData({plotId,plotImageId:result.plotImageId, imageOverlayId:result.imageOverlayId});
}

export class ImageViewerLayout extends PureComponent {
Expand Down
13 changes: 9 additions & 4 deletions src/firefly/js/visualize/PlotViewUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {getWavelength, isWLAlgorithmImplemented, PLANE} from './projection/Wavel
import {getNumberHeader, HdrConst} from './FitsHeaderUtil.js';
import {computeDistance, getRotationAngle, isCsysDirMatching, isEastLeftOfNorth, isPlotNorth} from './VisUtil';
import {removeRawData} from './rawData/RawDataCache.js';
import {MAX_DIRECT_IMAGE_SIZE, MAX_RAW_IMAGE_SIZE} from './rawData/RawDataCommon.js';
import {MAX_DIRECT_IMAGE_SIZE, MAX_DIRECT_MASK_IMAGE_SIZE, MAX_RAW_IMAGE_SIZE} from './rawData/RawDataCommon.js';
import {hasClearedDataInStore, hasLocalRawDataInStore, hasLocalStretchByteDataInStore} from './rawData/RawDataOps.js';


Expand Down Expand Up @@ -227,7 +227,10 @@ export const getOverlayByPvAndId = (ref,plotId,imageOverlayId) =>



export const removeRawDataByPlotView= (pv) => pv?.plots.forEach( (p) => removeRawData(p.plotImageId));
export function removeRawDataByPlotView(pv) {
pv?.plots.forEach( (p) => removeRawData(p.plotImageId));
pv?.overlayPlotViews?.forEach( (oPv) => oPv?.plot?.plotImageId && removeRawData(oPv.plot.plotImageId) );
}

/**
* construct an array of drawing layer from the store
Expand Down Expand Up @@ -1099,10 +1102,12 @@ export function canLoadStretchData(plot) {
return (plot.dataWidth*plot.dataHeight) < MAX_RAW_IMAGE_SIZE;
}

export function canLoadStretchDataDirect(plot) {
export function canLoadStretchDataDirect(plot, mask=false) {
if (!plot || !isImage(plot)) return false;
const size= (plot.dataWidth*plot.dataHeight);
return size<Math.min(MAX_RAW_IMAGE_SIZE,MAX_DIRECT_IMAGE_SIZE);
return mask ?
size<Math.min(MAX_RAW_IMAGE_SIZE,MAX_DIRECT_MASK_IMAGE_SIZE) :
size<Math.min(MAX_RAW_IMAGE_SIZE,MAX_DIRECT_IMAGE_SIZE);
}

export function isAllStretchDataLoadable(vr) {
Expand Down
2 changes: 1 addition & 1 deletion src/firefly/js/visualize/RelatedDataUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ function addMaskLayer(pv, maskNumber, hdu, fileKey, relatedData) {
const title= makeMaskTitle(maskNumber, relatedData.availableMask);

dispatchPlotMask({plotId:pv.plotId,
imageOverlayId:`${maskIdRoot}_#${maskNumber}_${maskCnt}`,
imageOverlayId:`${pv.plotId}-${maskIdRoot}_#${maskNumber}_${maskCnt}`,
fileKey, maskNumber, maskValue:Math.pow(2,Number(maskNumber)),
uiCanAugmentTitle:false, imageNumber:hdu, title,
relatedDataId, lazyLoad:true});
Expand Down
32 changes: 26 additions & 6 deletions src/firefly/js/visualize/rawData/ManageRawDataThread.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,25 @@ async function doColorChange(payload) {
}


function convertToBits(ary) {
const retAry= new Uint8ClampedArray(Math.trunc(ary.length/8)+1);
const len= ary.length;
for(let i=0;(i<len);i++) {
if (ary[i]) {
retAry[Math.trunc(i / 8)] = retAry[Math.trunc(i / 8)] | (1 << (i % 8));
}
}
return retAry;
}

async function fetchByteDataArray(payload) {
const {plotImageId,plotStateSerialized, plotState, processHeader, dataWidth, dataHeight,
bias, contrast, cmdSrvUrl, rootUrl} = payload;
bias, contrast, cmdSrvUrl, rootUrl, mask= false, maskBits=0, maskColor=''} = payload;

try {
const start= Date.now();
const allTileAry= await callStretchedByteData(plotImageId, plotStateSerialized, plotState, dataWidth,dataHeight, cmdSrvUrl);
const allTileAry= await callStretchedByteData(plotImageId, plotStateSerialized, plotState,
dataWidth,dataHeight, mask, maskBits, cmdSrvUrl);
const rawTileDataGroup= createRawTileDataGroup(dataWidth,dataHeight);
if (plotState.isThreeColor()) {
const bands= plotState.getBands();
Expand All @@ -92,15 +103,21 @@ async function fetchByteDataArray(payload) {
}
}
else {
rawTileDataGroup.rawTileDataAry.forEach( (rt,idx) => rt.pixelDataStandard= allTileAry[idx]);
if (mask) {
rawTileDataGroup.rawTileDataAry.forEach( (rt,idx) => rt.pixelDataStandard= convertToBits(allTileAry[idx]));
}
else {
rawTileDataGroup.rawTileDataAry.forEach( (rt,idx) => rt.pixelDataStandard= allTileAry[idx]);
}
}
let entry= getEntry(plotImageId);
if (!entry) {
addRawDataToCache(plotImageId,undefined,undefined,undefined,processHeader);
entry= getEntry(plotImageId);
}
const {retRawTileDataGroup, localRawTileDataGroup}=
await populateRawImagePixelDataInWorker(rawTileDataGroup, plotState.colorTableId, plotState.isThreeColor(), bias, contrast, {}, rootUrl);
await populateRawImagePixelDataInWorker(rawTileDataGroup, plotState.colorTableId, plotState.isThreeColor(),
mask, maskColor, bias, contrast, {}, rootUrl);
entry.rawTileDataGroup= localRawTileDataGroup;


Expand All @@ -120,12 +137,14 @@ async function fetchByteDataArray(payload) {
}


export async function callStretchedByteData(plotImageId,plotStateSerialized,plotState, dataWidth,dataHeight,cmdSrvUrl) {
export async function callStretchedByteData(plotImageId,plotStateSerialized,plotState, dataWidth,dataHeight,mask,maskBits,cmdSrvUrl) {

const options= makeFetchOptions(plotImageId, {
[ServerParams.COMMAND]: ServerParams.GET_BYTE_DATA,
[ServerParams.STATE] : plotStateSerialized,
[ServerParams.TILE_SIZE] : TILE_SIZE,
[ServerParams.MASK_DATA] : mask,
[ServerParams.MASK_BITS] : maskBits,

});

Expand Down Expand Up @@ -184,7 +203,8 @@ async function changeLocalRawDataColor(plotImageId, colorTableId, threeColor, bi
const newPlotState = plotState.copy();
newPlotState.colorTableId = colorTableId;
const {retRawTileDataGroup, localRawTileDataGroup}=
await populateRawImagePixelDataInWorker(entry.rawTileDataGroup, colorTableId, threeColor, bias, contrast, bandUse, rootUrl);
await populateRawImagePixelDataInWorker(entry.rawTileDataGroup, colorTableId, threeColor, false, '',
bias, contrast, bandUse, rootUrl);
entry.rawTileDataGroup= localRawTileDataGroup;
return {rawTileDataGroup:retRawTileDataGroup, plotStateSerialized: newPlotState.toJson(false)};
}
Expand Down
11 changes: 6 additions & 5 deletions src/firefly/js/visualize/rawData/RawDataCommon.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,20 @@ const abortControllers= new Map(); // map of imagePlotId and AbortController
export const TILE_SIZE = 3000;
export const MAX_RAW_IMAGE_SIZE = 400*MEG; // 400 megs
export const MAX_DIRECT_IMAGE_SIZE= 250*K;
export const MAX_DIRECT_MASK_IMAGE_SIZE= 200*MEG;
const USE_GPU = true;

async function populateRawTileDataArray(rawTileDataAry, colorModel, isThreeColor, bias, contrast, bandUse, rootUrl) {
async function populateRawTileDataArray(rawTileDataAry, colorModel, isThreeColor, mask, maskColor, bias, contrast, bandUse, rootUrl) {
const GPU = USE_GPU ? await getGpuJs(rootUrl) : undefined;
const createTransitionalTile = USE_GPU ? getGPUOps(GPU).createTransitionalTileWithGPU : createTransitionalTileWithCPU;
const presult = rawTileDataAry.map((id) => createTransitionalTile(id, colorModel, isThreeColor, bias, contrast, bandUse));
const presult = rawTileDataAry.map((id) => createTransitionalTile(id, colorModel, isThreeColor, mask, maskColor, bias, contrast, bandUse));
return await Promise.all(presult);
}

export async function populateRawImagePixelDataInWorker(rawTileDataGroup, colorTableId, isThreeColor, bias, contrast, bandUse, rootUrl) {
if (isGPUAvailableInWorker()) {
export async function populateRawImagePixelDataInWorker(rawTileDataGroup, colorTableId, isThreeColor, mask, maskColor, bias, contrast, bandUse, rootUrl) {
if (isGPUAvailableInWorker() && !mask) {
const colorModel = getColorModel(colorTableId);
const rawTileDataAry = await populateRawTileDataArray(rawTileDataGroup.rawTileDataAry, colorModel, isThreeColor, bias, contrast, bandUse, rootUrl);
const rawTileDataAry = await populateRawTileDataArray(rawTileDataGroup.rawTileDataAry, colorModel, isThreeColor, mask, maskColor, bias, contrast, bandUse, rootUrl);


const localRawTileDataGroup = {...rawTileDataGroup, rawTileDataAry, colorTableId};
Expand Down
Loading