/* Copyright 2013 The jeo project. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.jeo.tile;
import java.util.Arrays;
import java.util.Locale;
/**
* A map tile, as defined by {@linkplain http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification}.
*/
public class Tile {
Integer z, x, y;
byte[] data;
String mimeType;
private static final int[] JPG = new int[] { 0xFF, 0xD8, 0xFF };
private static final int[] PNG = new int[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
/**
* Constructs an empty tile object.
*/
public Tile() {
this(null, null, null);
}
/**
* Constructs an tile object from an index.
*/
public Tile(Integer z, Integer x, Integer y) {
this(z, x, y, null, null);
}
/**
* Constructs a tile object from its tile index, bounds, and image data.
*/
public Tile(Integer z, Integer x, Integer y, byte[] data, String mimeType) {
super();
this.z = z;
this.x = x;
this.y = y;
this.data = data;
this.mimeType = mimeType;
}
/**
* Constructs a tile by copying another tile.
*/
public Tile(Tile t) {
this(t.z, t.x, t.y, t.data, t.mimeType);
}
/**
* The z value (zoom level) of the tile.
*/
public Integer z() {
return z;
}
/**
* Sets the z value (zoom level) of the tile.
*/
public Tile z(Integer z) {
this.z = z;
return this;
}
/**
* The x value (column index) of the tile.
*/
public Integer x() {
return x;
}
/**
* Sets the x value (column index) of the tile.
*/
public Tile x(Integer x) {
this.x = x;
return this;
}
/**
* The y value (row index) of the tile.
*/
public Integer y() {
return y;
}
/**
* Sets the y value (row index) of the tile.
*/
public Tile y(Integer y) {
this.y = y;
return this;
}
/**
* The tile image data.
*/
public byte[] data() {
return data;
}
/**
* Sets the tile image data.
*/
public Tile data(byte[] data) {
this.data = data;
return this;
}
/**
* The mime type specifying the format of the tile image data.
*/
public String mimeType() {
if (mimeType == null && data != null) {
// try sniffing the data
if (isMagic(JPG)) {
mimeType = "image/jpg";
} else if (isMagic(PNG)) {
mimeType = "image/png";
}
}
return mimeType;
}
/**
* Sets the mime type specifying the format of the tile image data.
*/
public Tile mimeType(String mimeType) {
this.mimeType = mimeType;
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(data);
result = prime * result + ((mimeType == null) ? 0 : mimeType.hashCode());
result = prime * result + ((x == null) ? 0 : x.hashCode());
result = prime * result + ((y == null) ? 0 : y.hashCode());
result = prime * result + ((z == null) ? 0 : z.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tile other = (Tile) obj;
if (!Arrays.equals(data, other.data))
return false;
if (mimeType == null) {
if (other.mimeType != null)
return false;
} else if (!mimeType.equals(other.mimeType))
return false;
if (x == null) {
if (other.x != null)
return false;
} else if (!x.equals(other.x))
return false;
if (y == null) {
if (other.y != null)
return false;
} else if (!y.equals(other.y))
return false;
if (z == null) {
if (other.z != null)
return false;
} else if (!z.equals(other.z))
return false;
return true;
}
@Override
public String toString() {
return String.format(Locale.ROOT,"(z=%d, x=%d, y=%d)", z, y, x);
}
private boolean isMagic(int[] bytes) {
boolean match = data.length > bytes.length;
for (int i = 0; i < bytes.length && match; i++) {
match &= bytes[i] == data[i];
}
return match;
}
}