ClimateDataServiceImpl.java
/*
* Copyright 2018 Global Crop Diversity Trust
*
* 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 org.genesys.server.service.impl;
import java.lang.reflect.InvocationTargetException;
import java.nio.MappedByteBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.genesys.server.api.model.WorldclimJson;
import org.genesys.server.model.genesys.AccessionId;
import org.genesys.server.model.impl.TileClimate;
import org.genesys.server.persistence.AccessionIdRepository;
import org.genesys.server.persistence.TileClimateRepository;
import org.genesys.server.service.ClimateDataService;
import org.genesys.worldclim.WorldClimUtil;
import org.genesys.worldclim.grid.generic.Header;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* The ClimateDataService implementation.
*/
@Service
@Transactional(readOnly = true)
public class ClimateDataServiceImpl implements ClimateDataService {
public static final Logger LOG = LoggerFactory.getLogger(ClimateDataServiceImpl.class);
/** The tile climate repository. */
@Autowired
private TileClimateRepository tileClimateRepository;
/** The accessionId repository. */
@Autowired
private AccessionIdRepository accessionIdRepository;
/* (non-Javadoc)
* @see org.genesys.server.service.DSService#updateAccessionTileIndex(java.util.List)
*/
@Override
@Transactional
public void updateAccessionTileIndex(List<AccessionId> toSave) {
accessionIdRepository.saveAll(toSave);
}
/* (non-Javadoc)
* @see org.genesys.server.service.ClimateDataService#worldclimUpdate(java.lang.String, java.util.HashSet, java.nio.MappedByteBuffer, short, double)
*/
@Override
@Transactional(timeout = 30)
public void worldclimUpdate(String variableName, HashSet<Long> tileIndexes, MappedByteBuffer buffer, Header header, double factor) {
LOG.debug("Updating {} for tileIndexes: {}", variableName, tileIndexes.size());
short nullValue = (short) header.getNoDataValue();
Map<Long, TileClimate> rowMap = findOrMakeRows(tileIndexes);
for (final Long genesysTileIndex : tileIndexes) {
if (genesysTileIndex == null) {
continue;
}
TileClimate tileClimate = rowMap.get(genesysTileIndex);
if (tileClimate == null) {
LOG.warn("No TileClimate for {}", genesysTileIndex);
continue;
}
Long bilTileIndex = WorldClimUtil.tileIndexToDataIndex(genesysTileIndex);
if (bilTileIndex == null) {
LOG.debug("Antarctica not covered");
rowMap.remove(genesysTileIndex);
} else if (bilTileIndex * 2 > buffer.capacity() - 2) {
LOG.info("OUT OF FILE tile={}", bilTileIndex);
rowMap.remove(genesysTileIndex);
} else {
try {
short val = buffer.getShort((int) (bilTileIndex * 2));
if (val != nullValue) {
LOG.trace("tile={} val={}", bilTileIndex, val);
Double value = null;
if (factor == 1.0) {
value = Double.valueOf((long) (factor * val));
} else {
value = Double.valueOf(factor * val);
}
try {
var current = PropertyUtils.getProperty(tileClimate, variableName);
if (current == null || !current.equals(value)) {
BeanUtils.setProperty(tileClimate, variableName, value);
} else {
rowMap.remove(genesysTileIndex); // Need to update
}
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
LOG.error("Cannot set TileClimate#{} to {}. {}", variableName, value, e.getMessage());
}
} else {
// don't save in case of NODATA
rowMap.remove(genesysTileIndex);
}
} catch (IndexOutOfBoundsException e) {
LOG.error("OUT OF BOUND genesysTile={} dataTile={} capacity={} limit={}", genesysTileIndex, bilTileIndex, buffer.capacity(), buffer.limit());
throw e;
}
}
}
if (rowMap.size() > 0) {
LOG.debug("Updating {} records of {}", rowMap.size(), variableName);
tileClimateRepository.saveAll(rowMap.values());
}
LOG.debug("Done processing variable {}. {} records updated.", variableName, rowMap.size());
}
/**
* Find or make rows.
*
* @param tileIndexes the tile indexes
* @return the map
*/
private Map<Long, TileClimate> findOrMakeRows(HashSet<Long> tileIndexes) {
List<TileClimate> existingTiles = tileClimateRepository.findAllById(tileIndexes);
HashMap<Long, TileClimate> map = new HashMap<>(tileIndexes.size());
existingTiles.forEach((tile) -> {
map.put(tile.getTileIndex(), tile);
});
tileIndexes.forEach(tileIndex -> {
if (tileIndex != null && !map.containsKey(tileIndex)) {
map.put(tileIndex, new TileClimate(tileIndex));
}
});
return map;
}
/* (non-Javadoc)
* @see org.genesys.server.service.ClimateDataService#climateForTile(java.lang.Long)
*/
@Override
public TileClimate climateForTile(Long tileIndex) {
return tileClimateRepository.findById(tileIndex).orElse(null);
}
/* (non-Javadoc)
* @see org.genesys.server.service.ClimateDataService#jsonForTile(java.lang.Long)
*/
@Override
public WorldclimJson jsonForTile(Long tileIndex) {
TileClimate tileClimate = tileClimateRepository.findById(tileIndex).orElse(null);
if (tileClimate == null)
return null;
WorldclimJson wc = new WorldclimJson();
wc.setPrecipitation(new Double[] { tileClimate.getPrec1(), tileClimate.getPrec2(), tileClimate.getPrec3(), tileClimate.getPrec4(), tileClimate.getPrec5(), tileClimate.getPrec6(), tileClimate.getPrec7(), tileClimate.getPrec8(), tileClimate.getPrec9(), tileClimate.getPrec10(), tileClimate.getPrec11(), tileClimate.getPrec12() } );
wc.setTempMin(new Double[] { tileClimate.getTmin1(), tileClimate.getTmin2(), tileClimate.getTmin3(), tileClimate.getTmin4(), tileClimate.getTmin5(), tileClimate.getTmin6(), tileClimate.getTmin7(), tileClimate.getTmin8(), tileClimate.getTmin9(), tileClimate.getTmin10(), tileClimate.getTmin11(), tileClimate.getTmin12() } );
wc.setTempMean(new Double[] { tileClimate.getTmean1(), tileClimate.getTmean2(), tileClimate.getTmean3(), tileClimate.getTmean4(), tileClimate.getTmean5(), tileClimate.getTmean6(), tileClimate.getTmean7(), tileClimate.getTmean8(), tileClimate.getTmean9(), tileClimate.getTmean10(), tileClimate.getTmean11(), tileClimate.getTmean12() } );
wc.setTempMax(new Double[] { tileClimate.getTmax1(), tileClimate.getTmax2(), tileClimate.getTmax3(), tileClimate.getTmax4(), tileClimate.getTmax5(), tileClimate.getTmax6(), tileClimate.getTmax7(), tileClimate.getTmax8(), tileClimate.getTmax9(), tileClimate.getTmax10(), tileClimate.getTmax11(), tileClimate.getTmax12() } );
return wc;
}
}