/* Copyright 2014 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.data.mem; import io.jeo.geom.Bounds; import io.jeo.raster.Band; import io.jeo.raster.Band.Color; import io.jeo.data.Driver; import io.jeo.raster.DataBuffer; import io.jeo.raster.DataType; import io.jeo.raster.Raster; import io.jeo.raster.RasterDataset; import io.jeo.raster.RasterQuery; import io.jeo.raster.Stats; import io.jeo.util.Dimension; import io.jeo.util.Key; import io.jeo.util.Rect; import org.osgeo.proj4j.CoordinateReferenceSystem; import java.io.IOException; import java.lang.reflect.Array; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; public class MemRasterDataset implements RasterDataset { String name; Bounds bounds; CoordinateReferenceSystem crs; List<MemBand> bands = new ArrayList<MemBand>(); public MemRasterDataset(String name, Bounds bounds, CoordinateReferenceSystem crs) { this.name = name; this.bounds = bounds; this.crs = crs; } public void addBand(String name, Color color, DataType datatype, Object data) { if (data == null || !data.getClass().isArray()) { throw new IllegalArgumentException("data must be a non-null array"); } if (Array.getLength(data) == 0 || !Array.get(data, 0).getClass().isArray() || Array.getLength(Array.get(data,0)) == 0) { throw new IllegalArgumentException("data must be two dimensional array of non-zero size"); } Array2D<Number> array = new Array2D<Number>(data); if (!bands.isEmpty()) { Array2D first = bands.get(0).data; if (first.length() != array.length() && first.length(0) != array.length(0)) { throw new IllegalArgumentException(String.format(Locale.ROOT, "data dimensions must match existing band: (%d,%d), was (%d,%d)", first.length(0), first.length(), array.length(0), array.length())); } } bands.add(new MemBand(name, color, datatype, array)); } @Override public String name() { return name; } @Override public Driver<?> driver() { return new Memory(); } @Override public Map<Key<?>, Object> driverOptions() { return Collections.EMPTY_MAP; } @Override public CoordinateReferenceSystem crs() throws IOException { return crs; } @Override public Bounds bounds() throws IOException { return bounds; } @Override public void close() { } @Override public Dimension size() { if (bands.isEmpty()) { return new Dimension(0,0); } Array2D buf = bands.get(0).data; return new Dimension(buf.length(0), buf.length()); } @Override public List<Band> bands() throws IOException { return (List) bands; } @Override public Raster read(RasterQuery query) throws IOException { Raster raster = new Raster(); raster.bounds(bounds()).crs(crs()); Rect r = rect(); if (query.bounds() != null) { r = r.map(query.bounds(), raster.bounds()); raster.bounds(raster.bounds().intersection(query.bounds())); } Dimension size = query.size(); if (size == null) { // use the size of the query bounds if that is set if(query.bounds()!=null) { size = new Dimension(r.width(), r.height()); } else { size = size(); } } raster.size(size); List<Band> bands = null; if (query.bands() != null) { int[] b = query.bands(); bands = new ArrayList<Band>(b.length); for (int i : b) { bands.add(bands().get(i)); } } else { bands = bands(); } raster.bands(bands); DataType dataType = query.datatype(); if (dataType == null) { dataType = bands.get(0).datatype(); } DataBuffer buf = DataBuffer.create(r.width() * r.height(), dataType); buf.buffer().order(ByteOrder.LITTLE_ENDIAN); for (int y = r.top; y < r.bottom; y++) { for (int x = r.left; x < r.right; x++) { for (Band band : bands) { MemBand mb = (MemBand)band; buf.put(mb.data.get(y,x)); } buf.word(); } } if (!size.equals(r.size())) { buf = DataBuffer.resample(buf, r.size(), size); } return raster.data(buf.rewind()); } Rect rect() { return new Rect(0,0, size()); } static class Array2D<T> { Object array; Array2D(Object array) { this.array = array; } int length() { return Array.getLength(array); } int length(int dim) { return Array.getLength(Array.get(array, dim)); } T get(int i, int j) { return (T) Array.get(Array.get(array, i), j); } } static class MemBand implements Band { Array2D<Number> data; Color color; DataType datatype; String name; MemBand(String name, Color color, DataType datatype, Array2D<Number> data) { this.name = name; this.color = color; this.datatype = datatype; this.data = data; } @Override public String name() { return name; } @Override public Color color() { return color; } @Override public DataType datatype() { return datatype; } @Override public Double nodata() { return null; } @Override public Stats stats() throws IOException { Stats stats = new Stats(Double.MAX_VALUE, -Double.MAX_VALUE, 0d, 0d); int n = data.length() * data.length(0); for (int i = 0; i < data.length(); i++) { for (int j = 0; j < data.length(i); j++) { double val = data.get(i,j).doubleValue(); stats.max(Math.max(val, stats.max())); stats.min(Math.min(val, stats.min())); stats.mean(stats.mean()+val); } } //TODO: use a streaming algorithm to calculate stdev double mean = stats.mean() / ((double)n); stats.mean(mean); double stdev = 0; for (int i = 0; i < data.length(); i++) { for (int j = 0; j < data.length(i); j++) { double val = data.get(i,j).doubleValue(); double diff = mean - val; stdev += diff * diff; } } stats.stdev(Math.sqrt(stdev / ((double)n))); return stats; } } }