org.locationtech.geowave.adapter.raster.plugin.GeoWaveRasterReader Maven / Gradle / Ivy
/**
* Copyright (c) 2013-2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file distributed with this work for additional information regarding copyright
* ownership. All rights reserved. This program and the accompanying materials are made available
* under the terms of the Apache License, Version 2.0 which accompanies this distribution and is
* available at http://www.apache.org/licenses/LICENSE-2.0.txt
*/
package org.locationtech.geowave.adapter.raster.plugin;
import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.imageio.ImageReadParam;
import javax.media.jai.Histogram;
import javax.media.jai.ImageLayout;
import javax.media.jai.Interpolation;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.data.DataSourceException;
import org.geotools.factory.Hints;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.parameter.Parameter;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.BufferedCoordinateOperationFactory;
import org.geotools.util.Utilities;
import org.locationtech.geowave.adapter.auth.AuthorizationSPI;
import org.locationtech.geowave.adapter.raster.RasterUtils;
import org.locationtech.geowave.adapter.raster.Resolution;
import org.locationtech.geowave.adapter.raster.adapter.RasterDataAdapter;
import org.locationtech.geowave.adapter.raster.stats.HistogramStatistics;
import org.locationtech.geowave.adapter.raster.stats.OverviewStatistics;
import org.locationtech.geowave.adapter.raster.stats.RasterBoundingBoxStatistics;
import org.locationtech.geowave.core.geotime.ingest.SpatialDimensionalityTypeProvider;
import org.locationtech.geowave.core.geotime.store.query.IndexOnlySpatialQuery;
import org.locationtech.geowave.core.geotime.store.statistics.BoundingBoxDataStatistics;
import org.locationtech.geowave.core.geotime.util.GeometryUtils;
import org.locationtech.geowave.core.store.AdapterToIndexMapping;
import org.locationtech.geowave.core.store.CloseableIterator;
import org.locationtech.geowave.core.store.CloseableIterator.Wrapper;
import org.locationtech.geowave.core.store.adapter.AdapterIndexMappingStore;
import org.locationtech.geowave.core.store.adapter.InternalAdapterStore;
import org.locationtech.geowave.core.store.adapter.InternalDataAdapter;
import org.locationtech.geowave.core.store.adapter.PersistentAdapterStore;
import org.locationtech.geowave.core.store.adapter.statistics.DataStatisticsStore;
import org.locationtech.geowave.core.store.adapter.statistics.InternalDataStatistics;
import org.locationtech.geowave.core.store.api.DataStore;
import org.locationtech.geowave.core.store.api.DataTypeAdapter;
import org.locationtech.geowave.core.store.api.Index;
import org.locationtech.geowave.core.store.api.QueryBuilder;
import org.locationtech.geowave.core.store.index.IndexStore;
import org.locationtech.geowave.core.store.query.constraints.QueryConstraints;
import org.locationtech.geowave.core.store.util.DataStoreUtils;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.GeometryFactory;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.cs.AxisDirection;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.CoordinateOperationFactory;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** the reader gets the connection info and returns a grid coverage for every data adapter */
public class GeoWaveRasterReader extends AbstractGridCoverage2DReader implements
GridCoverage2DReader {
private static final Logger LOGGER = LoggerFactory.getLogger(GeoWaveRasterReader.class);
private GeoWaveRasterConfig config;
private PersistentAdapterStore geowaveAdapterStore;
private InternalAdapterStore geowaveInternalAdapterStore;
private DataStatisticsStore geowaveStatisticsStore;
private DataStore geowaveDataStore;
private IndexStore geowaveIndexStore;
private AdapterIndexMappingStore geowaveAdapterIndexMappingStore;
protected Map crsCache = new HashMap<>();
protected CoordinateReferenceSystem defaultCrs;
private AuthorizationSPI authorizationSPI;
protected static final CoordinateOperationFactory OPERATION_FACTORY =
new BufferedCoordinateOperationFactory(new Hints(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE));
private static Set UPDirections;
private static Set LEFTDirections;
// class initializer
static {
LEFTDirections = new HashSet<>();
LEFTDirections.add(AxisDirection.DISPLAY_LEFT);
LEFTDirections.add(AxisDirection.EAST);
LEFTDirections.add(AxisDirection.GEOCENTRIC_X);
LEFTDirections.add(AxisDirection.COLUMN_POSITIVE);
UPDirections = new HashSet<>();
UPDirections.add(AxisDirection.DISPLAY_UP);
UPDirections.add(AxisDirection.NORTH);
UPDirections.add(AxisDirection.GEOCENTRIC_Y);
UPDirections.add(AxisDirection.ROW_POSITIVE);
}
/**
* @param source The source object.
* @param uHints
* @throws IOException
* @throws MalformedURLException
* @throws AccumuloSecurityException
* @throws AccumuloException
*/
public GeoWaveRasterReader(final Object source, final Hints uHints) throws IOException {
super(source, uHints);
this.source = source;
if (GeoWaveGTRasterFormat.isParamList(source)) {
try {
config = GeoWaveRasterConfig.readFromConfigParams(source.toString());
} catch (final Exception e) {
throw new MalformedURLException(source.toString());
}
} else {
final URL url = GeoWaveGTRasterFormat.getURLFromSource(source);
if (url == null) {
throw new MalformedURLException(source.toString());
}
try {
config = GeoWaveRasterConfig.readFromURL(url);
} catch (final Exception e) {
LOGGER.error("Cannot read config", e);
throw new IOException(e);
}
}
init(config);
}
public GeoWaveRasterReader(final GeoWaveRasterConfig config) throws DataSourceException {
super(new Object(), new Hints());
this.config = config;
init(config);
}
private void init(final GeoWaveRasterConfig config) {
geowaveDataStore = config.getDataStore();
geowaveAdapterStore = config.getAdapterStore();
geowaveStatisticsStore = config.getDataStatisticsStore();
geowaveIndexStore = config.getIndexStore();
geowaveAdapterIndexMappingStore = config.getAdapterIndexMappingStore();
geowaveInternalAdapterStore = config.getInternalAdapterStore();
authorizationSPI = config.getAuthorizationFactory().create(config.getAuthorizationURL());
}
/**
* Constructor.
*
* @param source The source object.
* @throws IOException
* @throws AccumuloSecurityException
* @throws AccumuloException
* @throws UnsupportedEncodingException
*/
public GeoWaveRasterReader(final Object source) throws IOException {
this(source, null);
}
protected CoordinateReferenceSystem getDefaultCrs() {
if (defaultCrs != null) {
return defaultCrs;
}
if (!crsCache.isEmpty()) {
defaultCrs = crsCache.values().iterator().next();
} else {
final String[] coverageNames = getGridCoverageNames();
for (final String coverageName : coverageNames) {
final CoordinateReferenceSystem crs = getCrsForCoverage(coverageName);
if (crs != null) {
defaultCrs = crs;
break;
}
}
}
if (defaultCrs != null) {
return defaultCrs;
}
// if no data has been ingested yet with a CRS, this is the best guess
// we can make
return GeometryUtils.getDefaultCRS();
}
protected CoordinateReferenceSystem getCrsForCoverage(final String coverageName) {
CoordinateReferenceSystem crs = crsCache.get(coverageName);
if (crs != null) {
return crs;
}
final AdapterToIndexMapping adapterMapping =
geowaveAdapterIndexMappingStore.getIndicesForAdapter(getAdapterId(coverageName));
final Index[] indices = adapterMapping.getIndices(geowaveIndexStore);
if ((indices != null) && (indices.length > 0)) {
crs = GeometryUtils.getIndexCrs(indices[0]);
crsCache.put(coverageName, crs);
}
return crs;
}
@Override
public Format getFormat() {
return new GeoWaveGTRasterFormat();
}
@Override
public String[] getGridCoverageNames() {
try (final CloseableIterator> it = geowaveAdapterStore.getAdapters()) {
final List coverageNames = new ArrayList<>();
while (it.hasNext()) {
final DataTypeAdapter> adapter = it.next().getAdapter();
if (adapter instanceof RasterDataAdapter) {
coverageNames.add(((RasterDataAdapter) adapter).getCoverageName());
}
}
return coverageNames.toArray(new String[coverageNames.size()]);
}
}
@Override
public int getGridCoverageCount() {
try (final CloseableIterator> it = geowaveAdapterStore.getAdapters()) {
int coverageCount = 0;
while (it.hasNext()) {
final DataTypeAdapter> adapter = it.next().getAdapter();
if (adapter instanceof RasterDataAdapter) {
coverageCount++;
}
}
return coverageCount;
}
}
@Override
public String[] getMetadataNames() {
throw new UnsupportedOperationException(
"A coverage name must be provided, there is no support for a default coverage");
}
@Override
public String[] getMetadataNames(final String coverageName) {
if (!checkName(coverageName)) {
LOGGER.warn("Unable to find data adapter for '" + coverageName + "'");
return null;
}
final DataTypeAdapter> adapter =
geowaveAdapterStore.getAdapter(getAdapterId(coverageName)).getAdapter();
final Set var = ((RasterDataAdapter) adapter).getMetadata().keySet();
return var.toArray(new String[var.size()]);
}
@Override
public String getMetadataValue(final String name) {
throw new UnsupportedOperationException(
"A coverage name must be provided, there is no support for a default coverage");
}
@Override
public String getMetadataValue(final String coverageName, final String name) {
if (!checkName(coverageName)) {
LOGGER.warn("Unable to find data adapter for '" + coverageName + "'");
return null;
}
final DataTypeAdapter> adapter =
geowaveAdapterStore.getAdapter(getAdapterId(coverageName)).getAdapter();
return ((RasterDataAdapter) adapter).getMetadata().get(name);
}
@Override
protected boolean checkName(final String coverageName) {
Utilities.ensureNonNull("coverageName", coverageName);
final DataTypeAdapter> adapter =
geowaveAdapterStore.getAdapter(getAdapterId(coverageName)).getAdapter();
return (adapter != null) && (adapter instanceof RasterDataAdapter);
}
@Override
public GeneralEnvelope getOriginalEnvelope() {
throw new UnsupportedOperationException(
"A coverage name must be provided, there is no support for a default coverage");
}
@Override
public GeneralEnvelope getOriginalEnvelope(final String coverageName) {
final Envelope envelope =
geowaveDataStore.aggregateStatistics(
RasterBoundingBoxStatistics.STATS_TYPE.newBuilder().setAuthorizations(
authorizationSPI.getAuthorizations()).dataType(coverageName).build());
if (envelope == null) {
final CoordinateReferenceSystem crs = getCoordinateReferenceSystem(coverageName);
final double minX = crs.getCoordinateSystem().getAxis(0).getMinimumValue();
final double maxX = crs.getCoordinateSystem().getAxis(0).getMaximumValue();
final double minY = crs.getCoordinateSystem().getAxis(1).getMinimumValue();
final double maxY = crs.getCoordinateSystem().getAxis(1).getMaximumValue();
final GeneralEnvelope env =
new GeneralEnvelope(new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY));
env.setCoordinateReferenceSystem(crs);
return env;
}
// try to use both the bounding box and the overview statistics to
// determine the width and height at the highest resolution
final GeneralEnvelope env =
new GeneralEnvelope(
new Rectangle2D.Double(
envelope.getMinX(),
envelope.getMinY(),
envelope.getWidth(),
envelope.getHeight()));
env.setCoordinateReferenceSystem(getCoordinateReferenceSystem(coverageName));
return env;
}
@Override
public CoordinateReferenceSystem getCoordinateReferenceSystem() {
return getDefaultCrs();
}
@Override
public CoordinateReferenceSystem getCoordinateReferenceSystem(final String coverageName) {
return getCrsForCoverage(coverageName);
}
@Override
public GridEnvelope getOriginalGridRange() {
throw new UnsupportedOperationException(
"A coverage name must be provided, there is no support for a default coverage");
}
@Override
public GridEnvelope getOriginalGridRange(final String coverageName) {
try (CloseableIterator> statisticsIt =
geowaveStatisticsStore.getDataStatistics(
getAdapterId(coverageName),
RasterBoundingBoxStatistics.STATS_TYPE,
authorizationSPI.getAuthorizations())) {
int width = 0;
int height = 0;
// try to use both the bounding box and the overview statistics to
// determine the width and height at the highest resolution
InternalDataStatistics, ?, ?> statistics = null;
if (statisticsIt.hasNext()) {
statistics = statisticsIt.next();
}
if ((statistics != null) && (statistics instanceof BoundingBoxDataStatistics)) {
final BoundingBoxDataStatistics, ?> bboxStats =
(BoundingBoxDataStatistics, ?>) statistics;
try (CloseableIterator> overviewStatisticsIt =
geowaveStatisticsStore.getDataStatistics(
getAdapterId(coverageName),
OverviewStatistics.STATS_TYPE,
authorizationSPI.getAuthorizations())) {
statistics = null;
if (overviewStatisticsIt.hasNext()) {
statistics = overviewStatisticsIt.next();
}
if ((statistics != null) && (statistics instanceof OverviewStatistics)) {
final OverviewStatistics overviewStats = (OverviewStatistics) statistics;
width =
(int) Math.ceil(
((bboxStats.getMaxX() - bboxStats.getMinX())
/ overviewStats.getResolutions()[0].getResolution(0)));
height =
(int) Math.ceil(
((bboxStats.getMaxY() - bboxStats.getMinY())
/ overviewStats.getResolutions()[0].getResolution(1)));
}
}
}
return new GridEnvelope2D(0, 0, width, height);
}
}
@Override
public MathTransform getOriginalGridToWorld(final PixelInCell pixInCell) {
throw new UnsupportedOperationException(
"A coverage name must be provided, there is no support for a default coverage");
}
@Override
public MathTransform getOriginalGridToWorld(
final String coverageName,
final PixelInCell pixInCell) {
// just reuse super class implementation but ensure that we do not use a
// cached raster2model
synchronized (this) {
raster2Model = null;
return super.getOriginalGridToWorld(coverageName, pixInCell);
}
}
@Override
public GridCoverage2D read(final GeneralParameterValue[] parameters)
throws IllegalArgumentException, IOException {
throw new UnsupportedOperationException(
"A coverage name must be provided, there is no support for a default coverage");
}
/*
* (non-Javadoc)
*
* @see org.opengis.coverage.grid.GridCoverageReader#read(org.opengis.parameter
* .GeneralParameterValue [])
*/
@Override
public GridCoverage2D read(final String coverageName, final GeneralParameterValue[] params)
throws IOException {
if (!checkName(coverageName)) {
LOGGER.warn("Unable to find data adapter for '" + coverageName + "'");
return null;
}
final Date start = new Date();
// /////////////////////////////////////////////////////////////////////
//
// Checking params
//
// /////////////////////////////////////////////////////////////////////
Color outputTransparentColor = null;
Color backgroundColor = null;
Interpolation interpolation = null;
Rectangle dim = null;
GeneralEnvelope requestedEnvelope = null;
if (params != null) {
for (final GeneralParameterValue generalParameterValue : params) {
final Parameter