/*
* The Unified Mapping Platform (JUMP) is an extensible, interactive GUI
* for visualizing and manipulating spatial features with geometry and attributes.
*
* JUMP is Copyright (C) 2003 Vivid Solutions
*
* This program implements extensions to JUMP and is
* Copyright (C) 2004 Integrated Systems Analysts, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* For more information, contact:
*
* Integrated Systems Analysts, Inc.
* 630C Anchors St., Suite 101
* Fort Walton Beach, Florida 32548
* USA
*
* (850)862-7321
* www.ashs.isa.com
*
* 02.04.2006 changed by sstein to make compilation with free JVMs possible (see below)
*/
package org.openjump.io;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.FileImageInputStream;
import org.openjump.core.ui.plugin.layer.AddSIDLayerPlugIn;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.util.Assert;
import com.vividsolutions.jump.I18N;
import com.vividsolutions.jump.util.Blackboard;
import com.vividsolutions.jump.workbench.model.LayerManager;
import com.vividsolutions.jump.workbench.model.WMSLayer;
import com.vividsolutions.jump.workbench.plugin.PlugInContext;
import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
import com.vividsolutions.jump.workbench.ui.Viewport;
public class SIDLayer extends WMSLayer
{
final static String sLayer = I18N.get("org.openjump.io.SIDLayer.Layer");
final static String couldNotGetSIDinfoFor = I18N.get("org.openjump.io.SIDLayer.Could-not-get-SID-info-for");
final static String sLevel = I18N.get("org.openjump.io.SIDLayer.level");
final static String sOf = I18N.get("org.openjump.io.SIDLayer.of");
private SID_Info_List sidInfoList;
private List imageFilenames = new ArrayList();
private List deletedSIDs = new ArrayList();
private int sidPixelWidth = 0;
private int sidPixelHeight = 0;
private double sid_xres = 1;
private double sid_xrot = 0;
private double sid_yrot = 0;
private double sid_yres = 1;
private double sid_ulx = 0; //realworld coords
private double sid_uly = 0; //realworld coords
private String sid_colorspace;
private int maxLevel = 0;
private PlugInContext callingContext;
/**
* Called by Java2XML
*/
public SIDLayer()
{
sidInfoList = new SID_Info_List();
}
public SIDLayer(PlugInContext context, List layerNames) throws IOException
{ //this code copied from WMSLayer and AbstractLayerable constructors
callingContext = context;
LayerManager layerManager = context.getLayerManager();
String name = "MrSID " + sLayer;
Assert.isTrue(name != null);
Assert.isTrue(layerManager != null);
setLayerManager(layerManager);
//Can't fire events because this Layerable hasn't been added to the
//LayerManager yet. [Jon Aquino]
boolean firingEvents = layerManager.isFiringEvents();
layerManager.setFiringEvents(false);
try {
setName(layerManager.uniqueLayerName(name));
} finally {
layerManager.setFiringEvents(firingEvents);
}
sidInfoList = new SID_Info_List(layerNames);
}
private int round(double num)
{
return (int)Math.round(num);
}
public Image createImage(LayerViewPanel panel) throws IOException
{
//view and panel refer to the workbench portion with which the user is interacting
//raster refers to the visible portion of the SID file drawn onto the view panel
//image refers to the created image onto which is drawn the raster extracted from the SID file
totalBounds = new Envelope();
BufferedImage newImage = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D)newImage.getGraphics();
g.setColor(new Color(0,0,0,0)); //alpha channel = 0 for transparent background
g.fillRect(0, 0, panel.getWidth(), panel.getHeight());
List imageFilenames = sidInfoList.getFileNames();
for (Iterator i = imageFilenames.iterator(); i.hasNext();)
{
Object currObj = i.next();
String sidFilename = (String) currObj;
SID_Info sidInfo = sidInfoList.getInfo(sidFilename);
if (sidInfo == null)
{
callingContext.getWorkbenchFrame().getOutputFrame().addText("Could not get SID info for " + sidFilename);
}
else
{
int sidPixelWidth = sidInfo.getPixelWidth();
int sidPixelHeight = sidInfo.getPixelHeight();
double sid_xres = sidInfo.getXRes();
double sid_ulx = sidInfo.getULX(); //realworld coords
double sid_uly = sidInfo.getULY(); //realworld coords
int image_x = 0; //x position of raster in final image in pixels
int image_y = 0; //y position of raster in final image in pixels
int image_w = panel.getWidth(); //width of raster in final image in pixels
int image_h = panel.getHeight(); //height of raster in final image in pixels
Viewport viewport = panel.getViewport();
Envelope vpEnvelope = viewport.getEnvelopeInModelCoordinates();
double view_res = 1 / viewport.getScale(); //panel resolution
double rwViewLeft = vpEnvelope.getMinX();
double rwViewRight = vpEnvelope.getMaxX();
double rwViewTop = vpEnvelope.getMaxY();
double rwViewBot = vpEnvelope.getMinY();
//java.awt.Toolkit.getDefaultToolkit().beep();
//Here calculate the real world sid edges for level zero.
//These will be recalculated for the final level later in the code
//since the real world edges will walk away from the original edges
//as we go to higher levels.
//see paper on Georeferencing images.
double halfPixel = 0.5 * sid_xres;
double rwSidFileLeftEdge = sid_ulx - halfPixel;
double rwSidFileRightEdge = rwSidFileLeftEdge + (sidPixelWidth * sid_xres);
double rwSidFileTopEdge = sid_uly + halfPixel;
double rwSidFileBotEdge = rwSidFileTopEdge - (sidPixelHeight * sid_xres);
totalBounds.expandToInclude(rwSidFileLeftEdge, rwSidFileBotEdge);
totalBounds.expandToInclude(rwSidFileRightEdge, rwSidFileTopEdge);
double rwRasterLeft = Math.max(rwViewLeft, rwSidFileLeftEdge);
double rwRasterRight = Math.min(rwViewRight, rwSidFileRightEdge);
double rwRasterTop = Math.min(rwViewTop, rwSidFileTopEdge);
double rwRasterBot = Math.max(rwViewBot, rwSidFileBotEdge);
//calculate the sid level which will return the number of pixels
//that is closest to the number of view pixels so that we can
//minimize the amount of needed stretching to make the file fit the view.
double rwViewWidth = rwViewRight - rwViewLeft;
double widthInFilePixels = rwViewWidth / sid_xres; //file pixels
double widthInViewPixels = rwViewWidth / view_res; //view pixels
int sidLevel = (int)Math.round(Math.log(widthInFilePixels / widthInViewPixels) / Math.log(2));
if (sidLevel < 0) sidLevel = 0;
if (sidLevel > maxLevel) sidLevel = maxLevel;
double lvlres = sid_xres * Math.pow(2, sidLevel);
panel.getContext().setStatusMessage("MrSID " + sLevel + " " + sidLevel + " " + sOf + " " + maxLevel);
//calculate the number of pixels at this level
int lvl = 0;
int sidLvlPixelWidth = sidPixelWidth;
int sidLvlPixelHeight = sidPixelHeight;
while (lvl < sidLevel)
{
sidLvlPixelWidth = round(0.5 * sidLvlPixelWidth);
sidLvlPixelHeight = round(0.5 * sidLvlPixelHeight);
lvl++;
}
//now calculate the real world edges of the sid file at this level
halfPixel = 0.5 * lvlres;
rwSidFileLeftEdge = sid_ulx - halfPixel;
rwSidFileRightEdge = rwSidFileLeftEdge + (sidLvlPixelWidth * lvlres);
rwSidFileTopEdge = sid_uly + halfPixel;
rwSidFileBotEdge = rwSidFileTopEdge - (sidLvlPixelHeight * lvlres);
//check to see if this sid is inside the view area
if (!((rwSidFileRightEdge <= rwViewLeft) || (rwSidFileLeftEdge >= rwViewRight) || (rwSidFileTopEdge <= rwViewBot) || (rwSidFileBotEdge >= rwViewTop)))
{
int sidLeftPixel = (int)((rwRasterLeft - rwSidFileLeftEdge) / lvlres); //trunc
int sidRightPixel = (int)((rwRasterRight - rwSidFileLeftEdge) / lvlres); //trunc
if (sidRightPixel == sidLvlPixelWidth) sidRightPixel = sidLvlPixelWidth - 1;
int sidTopPixel = (int)((rwSidFileTopEdge - rwRasterTop) / lvlres); //trunc
int sidBotPixel = (int)((rwSidFileTopEdge - rwRasterBot) / lvlres); //trunc
if (sidBotPixel == sidLvlPixelHeight) sidBotPixel = sidLvlPixelHeight - 1;
double rwSidLeft = rwSidFileLeftEdge + (sidLeftPixel * lvlres);
double rwSidRight = rwSidFileLeftEdge + (sidRightPixel * lvlres) + lvlres;
double rwSidTop = rwSidFileTopEdge - (sidTopPixel * lvlres);
double rwSidBot = rwSidFileTopEdge - (sidBotPixel * lvlres) - lvlres;
int leftOffset = round((rwRasterLeft - rwSidLeft) / view_res);
int rightOffset = round((rwSidRight - rwRasterRight) / view_res);
int topOffset = round((rwSidTop - rwRasterTop) / view_res);
int botOffset = round((rwRasterBot - rwSidBot) / view_res);
int sid_x = sidLeftPixel;
int sid_y = sidTopPixel;
int sid_w = sidRightPixel - sidLeftPixel + 1;
if (sid_w <= 0) sid_w = 1;
int sid_h = sidBotPixel - sidTopPixel + 1;
if (sid_h <= 0) sid_h = 1;
image_x = round(rwRasterLeft / view_res) - round(rwViewLeft / view_res);
image_w = round(rwRasterRight / view_res) - round(rwRasterLeft / view_res);
if (image_w <= 0) image_w = 1;
image_y = round(rwViewTop / view_res) - round(rwRasterTop / view_res);
image_h = round(rwRasterTop / view_res) - round(rwRasterBot / view_res);
if (image_h <= 0) image_h = 1;
image_x -= leftOffset;
image_y -= topOffset;
image_w += (leftOffset + rightOffset);
image_h += (topOffset + botOffset);
File jpgDir = new File(AddSIDLayerPlugIn.TMP_PATH);
File jpgFile = File.createTempFile("Temp", ".jpg", jpgDir);
String jpgFilename = jpgFile.getCanonicalPath();
try
{
String [] runStr =
{
AddSIDLayerPlugIn.MRSIDDECODE,
//"C:\\ashsii\\jump\\etc\\mrsiddecode.exe",
"-i",
sidFilename,
"-s",
"" + sidLevel,
"-ulxy",
"" + sid_x,
"" + sid_y,
"-wh",
"" + sid_w,
"" + sid_h,
"-o",
jpgFilename,
"-jpg",
"-quiet",
"-coord",
"image"
};
Process p = Runtime.getRuntime().exec(runStr);
p.waitFor();
p.destroy();
} catch (Throwable t)
{
t.printStackTrace();
}
if (((jpgFile.exists()) && (jpgFile.isFile()) && (jpgFile.canRead())))
{
//-- [sstein 02.04.2006] changed to javax to work with free JavaVM
// as proposed by Petter Reinholdtsen (see jpp-devel 12.03.2006)
FileImageInputStream in = new FileImageInputStream(new File(jpgFilename));
ImageReader decoder = (ImageReader) ImageIO.getImageReadersByFormatName("JPEG").next();
decoder.setInput(in);
BufferedImage image = decoder.read(0);
decoder.dispose();
in.close();
if (!sid_colorspace.equals("GREYSCALE"))
{
RenderingHints rh = new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHints(rh);
}
g.setComposite(AlphaComposite.Src);
g.drawImage(image, image_x, image_y, image_w, image_h, panel);
new File(jpgFilename).delete(); //so they don't accumulate in the tmp dir
}
}
}
}
return newImage;
}
protected SID_Info readWorldFile(String filename) throws IOException, FileNotFoundException
{
String wfname = (filename.indexOf('.') < 0) ? filename : filename.substring(0, filename.indexOf('.'));
wfname = wfname + ".sdw";
File file = new File(wfname);
if (!file.exists()) return null;
if (!(file.isFile() && file.canRead())) return null;
FileReader wf = new FileReader(wfname);
BufferedReader in = new BufferedReader(wf);
double sid_xres = Double.parseDouble(in.readLine());
double sid_xrot = Double.parseDouble(in.readLine());
double sid_yrot = Double.parseDouble(in.readLine());
double sid_yres = Double.parseDouble(in.readLine());
double sid_ulx = Double.parseDouble(in.readLine());
double sid_uly = Double.parseDouble(in.readLine());
SID_Info sidInfo = new SID_Info(filename,
0,
0,
sid_xres,
sid_xrot,
sid_yrot,
sid_yres,
sid_ulx,
sid_uly);
in.close();
wf.close();
return sidInfo;
}
protected SID_Info readInfo(String sidFilename) //throws IOException
{
int sidPixelWidth = 0;
int sidPixelHeight = 0;
double sid_xres = 1;
double sid_xrot = 0;
double sid_yrot = 0;
double sid_yres = 1;
double sid_ulx = 0; //realworld coords
double sid_uly = 0; //realworld coords
maxLevel = 0;
String infoFilename = AddSIDLayerPlugIn.TMP_PATH + "MrSIDinfo.txt";
int numInfoItems = 0;
try
{
String [] runStr =
{
AddSIDLayerPlugIn.MRSIDINFO,
sidFilename,
"-sid",
"-quiet",
"-log",
infoFilename
};
Process p = Runtime.getRuntime().exec(runStr);
p.waitFor();
p.destroy();
File file = new File(infoFilename);
if (!(file.exists() && file.isFile() && file.canRead()))
return null; //this could happen if mrsidinfo.exe couldn't produce a file
//read the info
FileReader fin = new FileReader(infoFilename);
BufferedReader in = new BufferedReader(fin);
String lineIn = in.readLine();
while (in.ready())
{
String value = "";
if (lineIn.indexOf("width:") != -1)
{
value = lineIn.substring(lineIn.indexOf(":") + 1);
sidPixelWidth = Integer.parseInt(value.trim());
numInfoItems++;
}
if (lineIn.indexOf("height:") != -1)
{
value = lineIn.substring(lineIn.indexOf(":") + 1);
sidPixelHeight = Integer.parseInt(value.trim());
numInfoItems++;
}
if (lineIn.indexOf("number of levels:") != -1)
{
value = lineIn.substring(lineIn.indexOf(":") + 1);
maxLevel = Integer.parseInt(value.trim());
numInfoItems++;
}
if (lineIn.indexOf("X UL:") != -1)
{
value = lineIn.substring(lineIn.indexOf(":") + 1);
sid_ulx = Double.parseDouble(value.trim());
numInfoItems++;
}
if (lineIn.indexOf("Y UL:") != -1)
{
value = lineIn.substring(lineIn.indexOf(":") + 1);
sid_uly = Double.parseDouble(value.trim());
numInfoItems++;
}
if (lineIn.indexOf("X res:") != -1)
{
value = lineIn.substring(lineIn.indexOf(":") + 1);
sid_xres = Double.parseDouble(value.trim());
numInfoItems++;
}
if (lineIn.indexOf("Y res:") != -1)
{
value = lineIn.substring(lineIn.indexOf(":") + 1);
sid_yres = Double.parseDouble(value.trim());
numInfoItems++;
}
if (lineIn.indexOf("color space:") != -1)
{
value = lineIn.substring(lineIn.indexOf(":") + 1);
sid_colorspace = value.trim();
numInfoItems++;
}
lineIn = in.readLine();
}
in.close();
fin.close();
if (numInfoItems == 8)
return new SID_Info(sidFilename,
sidPixelWidth,
sidPixelHeight,
sid_xres,
sid_xrot,
sid_yrot,
sid_yres,
sid_ulx,
sid_uly);
else
return null;
} catch (Throwable t)
{
t.printStackTrace();
return null;
}
}
public void addImageFilename(String imageFilename)
{
sidInfoList.addInfo(imageFilename);
}
public List getImageFilenames()
{
return sidInfoList.getFileNames();
}
//[sstein 27.April.2008] not needed, and it causes an null pointer exception
// private Blackboard blackboard = new Blackboard();
// public Blackboard getBlackboard()
// {
// return blackboard;
// }
private class SID_Info
{
private String fileName;
private int pixelWidth;
private int pixelHeight;
private double xres;
private double xrot;
private double yrot;
private double yres;
private double ulx; //realworld coords
private double uly; //realworld coords
SID_Info(String fileName,
int pixelWidth,
int pixelHeight,
double xres,
double xrot,
double yrot,
double yres,
double ulx,
double uly)
{
this.fileName = fileName;
this.pixelWidth = pixelWidth;
this.pixelHeight = pixelHeight;
this.xres = xres;
this.xrot = xrot;
this.yrot = yrot;
this.yres = yres;
this.ulx = ulx;
this.uly = uly;
}
String getFileName()
{
return fileName;
}
int getPixelWidth()
{
return pixelWidth;
}
int getPixelHeight()
{
return pixelHeight;
}
double getXRes()
{
return xres;
}
double getXRot()
{
return xrot;
}
double getYRot()
{
return yrot;
}
double getYRes()
{
return yres;
}
double getULX()
{
return ulx;
}
double getULY()
{
return uly;
}
}
private class SID_Info_List
{
private Vector infoList = new Vector(50, 10);
SID_Info_List()
{
}
SID_Info_List(List fileNames)
{
for (Iterator i = fileNames.iterator(); i.hasNext();)
{
String fileName = (String) i.next();
SID_Info sidInfo = readInfo(fileName);
if (sidInfo == null)
callingContext.getWorkbenchFrame().getOutputFrame().addText("Could not get SID info for " + fileName);
else
infoList.add(sidInfo);
}
}
public void addInfo(String fileName)
{
SID_Info sidInfo = readInfo(fileName);
if (sidInfo != null)
infoList.add(sidInfo);
}
public SID_Info getInfo(String filename)
{
for (Iterator i = infoList.iterator(); i.hasNext();)
{
SID_Info sidInfo = (SID_Info) i.next();
if (sidInfo.getFileName().equals(filename))
return sidInfo;
}
return null;
}
public List getFileNames()
{
List imageFilenames = new ArrayList();
for (Iterator i = infoList.iterator(); i.hasNext();)
imageFilenames.add(((SID_Info) i.next()).getFileName());
return Collections.unmodifiableList(imageFilenames);
}
}
}