/*******************************************************************************
* Copyright (c) MOBAC developers
*
* 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, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package mobac.program.atlascreators.impl.rmp;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.IOException;
import mobac.program.atlascreators.tileprovider.TileProvider;
import mobac.program.interfaces.MapSpace;
import org.apache.log4j.Logger;
public class MobacTile {
private static final Logger log = Logger.getLogger(MobacTile.class);
private final TileProvider tileProvider;
private final int tilex;
private final int tiley;
private BufferedImage image;
private BoundingRect boundingRect;
public MobacTile(TileProvider tileProvider, MapSpace mapSpace, int tilex, int tiley, int zoom) {
this.tileProvider = tileProvider;
this.tilex = tilex;
this.tiley = tiley;
image = null;
int tileSize = mapSpace.getTileSize();
int x = tilex * tileSize;
int y = tiley * tileSize;
//double north = mapSpace.cYToLat(y, zoom);
//double south = mapSpace.cYToLat(y + tileSize - 1, zoom);
//double west = mapSpace.cXToLon(x, zoom);
//double east = mapSpace.cXToLon(x + tileSize - 1, zoom);
Point2D.Double p1 = mapSpace.cXYToLonLat(x, y, zoom);
Point2D.Double p2 = mapSpace.cXYToLonLat(x + tileSize - 1, y + tileSize - 1, zoom);
double north = p1.y;
double south = p2.y;
double west = p1.x;
double east = p2.x;
// north and south have to be negated - this really strange!
boundingRect = new BoundingRect(-north, -south, west, east);
}
/**
* Returns the image of the tile. Creates on if necessary
*/
public BufferedImage getImage() {
/* --- Load image if none is present --- */
if (image == null)
image = loadImage();
return image;
}
private BufferedImage loadImage() {
try {
image = tileProvider.getTileImage(tilex, tiley);
} catch (IOException e) {
log.error("", e);
image = createBlack(256, 256);
}
return image;
}
/**
* create a black Tile
*
* @return image of black square
*/
private BufferedImage createBlack(int width, int height) {
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics graph = img.getGraphics();
graph.setColor(Color.BLACK);
graph.fillRect(0, 0, width, height);
return img;
}
public void drawSubImage(BoundingRect dest_area, BufferedImage dest_image) {
BufferedImage src_image;
WritableRaster src_graph, dst_graph;
BoundingRect src_area;
int maxx, maxy;
double src_c_x, src_c_y;
int pix_x, pix_y;
BufferedImage imageBuffer;
Graphics graphics;
int[] pixel = new int[3];
/* --- Get the coordination rectangle of the source image --- */
src_area = boundingRect;
/* --- Get Graphics context --- */
src_image = getImage();
if (src_image == null)
return;
/* --- Convert it to RGB color space --- */
imageBuffer = new BufferedImage(src_image.getWidth(), src_image.getHeight(),
BufferedImage.TYPE_INT_RGB);
graphics = imageBuffer.createGraphics();
try {
graphics.drawImage(src_image, 0, 0, null);
} finally {
graphics.dispose();
}
src_graph = imageBuffer.getRaster();
dst_graph = dest_image.getRaster();
/*
* --- Iterate over all pixels of the destination image. Unfortunately
* we need this technique because source and dest do not have exactly
* the same zoom level, so the source image has to be compressed or
* expanded to match the destination image ---
*/
maxx = dest_image.getWidth();
maxy = dest_image.getHeight();
for (int y = 0; y < maxy; y++) {
/* --- Calculate the y-coordinate of the current line --- */
src_c_y = dest_area.getNorth() + (dest_area.getSouth() - dest_area.getNorth()) * y
/ maxy;
/* --- Calculate the pixel line of the source image --- */
pix_y = (int) ((src_c_y - src_area.getNorth()) * 256
/ (src_area.getSouth() - src_area.getNorth()) + 0.5);
/* --- Ignore line that are out of the source area --- */
if (pix_y < 0 || pix_y > 255)
continue;
// log.trace("scale factor y: " + (pix_y / (double) y));
for (int x = 0; x < maxx; x++) {
/* --- Calculate the x-coordinate of the current row --- */
src_c_x = dest_area.getWest() + (dest_area.getEast() - dest_area.getWest()) * x
/ maxx;
/* --- Calculate the pixel row of the source image --- */
pix_x = (int) ((src_c_x - src_area.getWest()) * 256
/ (src_area.getEast() - src_area.getWest()) + 0.5);
/* --- Ignore the row if it is outside the source area --- */
if (pix_x < 0 || pix_x > 255)
continue;
/* --- Transfer the pixel --- */
src_graph.getPixel(pix_x, pix_y, pixel);
dst_graph.setPixel(x, y, pixel);
}
}
}
public int getImageHeight() {
/* --- A tile is always 256 pixels high --- */
return 256;
}
public int getImageWidth() {
/* --- A tile is always 256 pixels wide --- */
return 256;
}
public BufferedImage getSubImage(BoundingRect area, int width, int height) {
BufferedImage result = createBlack(width, height);
drawSubImage(area, result);
return result;
}
@Override
public String toString() {
return String.format("MobacTile x/y [%d/%d] = %s", tilex, tiley, boundingRect);
}
}