it.geosolutions.geobatch.destination.vulnerability.TargetManager.java Source code

Java tutorial

Introduction

Here is the source code for it.geosolutions.geobatch.destination.vulnerability.TargetManager.java

Source

/*
 *  Copyright (C) 2007-2012 GeoSolutions S.A.S.
 *  http://www.geo-solutions.it
 *
 *  GPLv3 + Classpath exception
 *
 *  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 3 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 it.geosolutions.geobatch.destination.vulnerability;

import it.geosolutions.jaiext.range.Range;
import it.geosolutions.jaiext.stats.Statistics;
import it.geosolutions.jaiext.zonal.ZoneGeometry;

import java.awt.image.RenderedImage;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.ListUtils;
import org.apache.commons.collections.MapUtils;
import org.geotools.coverage.grid.GridGeometry2D;
import org.jaitools.media.jai.zonalstats.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class is responsible for managing the different types of Target (Human - NotHuman - GroundCoverage )
 * providing a single API.
 * The abstraction is implemented by the 3 inner class that are instantiated with the getInstance method.  
 * 
 * @author DamianoG
 * 
 */
public abstract class TargetManager {

    public static class TargetInfo {
        public TargetInfo(String id, GridGeometry2D gg2d, Double nodata, RenderedImage raster, TargetType type,
                Map vulnerabilityCfg, int pixelArea) {
            this.gg2d = gg2d;
            this.nodata = nodata;
            this.raster = raster;
            this.id = id;
            manager = TargetManager.createManager(id, type, vulnerabilityCfg, pixelArea);
        }

        public Double getNodata() {
            return nodata;
        }

        public RenderedImage getRaster() {
            return raster;
        }

        public TargetManager getManager() {
            return manager;
        }

        public GridGeometry2D getGG2D() {
            return gg2d;
        }

        public String getId() {
            return id;
        }

        GridGeometry2D gg2d;

        Double nodata;

        RenderedImage raster;

        String id;

        TargetManager manager;

        TargetInfo mergeWith(TargetInfo other) {
            if (manager instanceof GroundCoverageTarget && other.manager instanceof GroundCoverageTarget) {
                manager.pixelValues.putAll(other.manager.pixelValues);
                return this;
            }
            return null;
        }
    }

    private final static Logger LOGGER = LoggerFactory.getLogger(TargetManager.class);

    protected static Map<String, Double[]> allDistances;
    protected static List<Integer> copSuoloValues;

    static {
        /*allDistances = TargetPropertiesLoader.loadDistances();
         TargetPropertiesLoader tpl = new TargetPropertiesLoader();
         copSuoloValues = tpl.getAllCopSuoloValues();*/
    }

    /**
     * Boolean indicating if the target is human or not
     */
    protected boolean humanTarget;

    /**
     * The resolutions of the pixels
     */
    protected int pixelArea;

    /**
     * The accepted distances for this Target. The initTargets() method is responsible for init it.
     */
    protected Double[] distances;

    /**
     * The id of the target as specified in the properties file
     */
    protected String targetID;

    /**
     * The value of copSuolo pixels. This is initialized (with the static list) just if the concrete class is of type GroundCoverage.
     * Otherwise it must be null.
     */
    protected Map<Integer, String> pixelValues;

    private TargetManager(String targetID, Double[] distances, int pixelArea) {
        this.targetID = targetID;
        this.pixelArea = pixelArea <= 0 ? 100 : pixelArea;
        this.distances = distances;
    }

    /**
     * Handle the results and add them to the resultStatsMap provided as input. The band parameter is used for selecting the statistic associated with
     * the related target.
     * 
     * @param results
     * @param statsMap
     * @param forceToZero
     * @param band
     */
    public abstract void handleResults(List<Result> results, ResultStatsMap statsMap, boolean forceToZero);

    /**
     * Handle the results and add them to the resultStatsMap provided as input. The band parameter is used for selecting the statistic associated with
     * the related target.
     * 
     * @param results
     * @param statsMap
     * @param forceToZero
     * @param band
     */
    public abstract void handleResults(ZoneGeometry result, ResultStatsMap statsMap, boolean forceToZero, int band);

    /**
     * Check if the Target is Human or Not
     */
    public boolean isHumanTarget() {
        return humanTarget;
    }

    /**
     * Check if the provided input distance is accepted by this target
     * 
     * @param distance
     * @return
     */
    public boolean isDistanceRelatedToTarget(Double distance) {
        return Arrays.asList(distances).contains(distance);
    }

    public Map<Integer, String> getPixelValues() {
        if (pixelValues == null) {
            return null;
        }
        return MapUtils.unmodifiableMap(pixelValues);
    }

    public List<Double> getDistances() {
        if (distances == null) {
            return null;
        }
        return ListUtils.unmodifiableList(Arrays.asList(distances));
    }

    //**************************************
    //  Concrete Targets Implementation
    //**************************************

    /**
     * In The rasters of Human Targets each pixel contains the number of the humans present in that area.
     * So basically we have to take the result of the stat SUM and store it in the result Map   
     * 
     * @author DamianoG
     *
     */
    private static class HumanTarget extends TargetManager {

        /**
         * @param pixelArea
         */
        public HumanTarget(String targetID, Double[] distances, int pixelArea) {
            super(targetID, distances, pixelArea);
            humanTarget = true;
        }

        @Override
        public void handleResults(List<Result> results, ResultStatsMap statsMap, boolean forceToZero) {
            for (Result el3 : results) {
                Double resSimple = (el3.getValue().equals(Double.NaN) ? 0d : el3.getValue());
                // Long countSimple = el3.getNumOffered();
                statsMap.addResult(targetID, resSimple, forceToZero);
            }
        }

        public void handleResults(List<ZoneGeometry> results, ResultStatsMap statsMap, boolean forceToZero,
                int band) {
            // Default class value
            int classId = 0;

            int zones = results.size();

            for (int i = 0; i < zones; i++) {

                ZoneGeometry geometry = results.get(i);
                // Total statistics
                Map<Integer, Map<Integer, Map<Range, Statistics[]>>> totalStatistics = geometry.getTotalStats();
                // Statistics associates to the selected band and the class
                Map<Range, Statistics[]> resultsPerRange = totalStatistics.get(band).get(classId);
                // Set of Ranges(if not used, it is only one object)
                Set<Range> ranges = resultsPerRange.keySet();

                for (Range r : ranges) {
                    // Selection of the statistic
                    Statistics stat = resultsPerRange.get(r)[0];
                    // Sum of the values
                    Double resSimple = (Double) (stat.getResult().equals(Double.NaN) ? 0d : stat.getResult());
                    // Addition of the result
                    statsMap.addResult(targetID, resSimple, forceToZero);
                }
            }
        }

        @Override
        public void handleResults(ZoneGeometry result, ResultStatsMap statsMap, boolean forceToZero, int band) {
            // Default class value
            int classId = 0;
            // Total statistics
            Map<Integer, Map<Integer, Map<Range, Statistics[]>>> totalStatistics = result.getTotalStats();
            // Statistics associates to the selected band and the class
            Map<Range, Statistics[]> resultsPerRange = totalStatistics.get(band).get(classId);
            // Set of Ranges(if not used, it is only one object)
            Set<Range> ranges = resultsPerRange.keySet();

            for (Range r : ranges) {
                // Selection of the statistic
                Statistics stat = resultsPerRange.get(r)[0];
                // Sum of the values
                Double resSimple = (Double) (stat.getResult().equals(Double.NaN) ? 0d : stat.getResult());
                // Addition of the result
                statsMap.addResult(targetID, resSimple, forceToZero);
            }
        }
    }

    /**
     * In The rasters of NotHuman Targets each pixel that is different to NODATA represent a snippet of the area of that target
     * We want calculate the mq2 present inside a given ROI so we have to count the pixel used for calculate the stat sum and multiply that value for the resolution of the pixel
     * So basically we have to take the result of the stat SUM and store it in the result Map
     * 
     * @author DamianoG
     *
     */
    private static class NotHumanTarget extends TargetManager {

        /**
         * @param pixelArea
         */
        public NotHumanTarget(String targetID, Double[] distances, int pixelArea) {
            super(targetID, distances, pixelArea);
            humanTarget = false;
        }

        @Override
        public void handleResults(List<Result> results, ResultStatsMap statsMap, boolean forceToZero) {
            for (Result el3 : results) {
                // Double resSimple = (el3.getValue().equals(Double.NaN)?0d:el3.getValue());
                // TODO check if must be used numOffered or numAccepted for retrieve the number of valid (without NODATA) pixels used for the stats
                Long countSimple = el3.getNumAccepted();
                statsMap.addResult(targetID, Double.valueOf(countSimple) * pixelArea, forceToZero);
            }
        }

        public void handleResults(List<ZoneGeometry> results, ResultStatsMap statsMap, boolean forceToZero,
                int band) {

            // Default class value
            int classId = 0;

            for (ZoneGeometry geometry : results) {
                // Total statistics
                Map<Integer, Map<Integer, Map<Range, Statistics[]>>> totalStatistics = geometry.getTotalStats();
                // Statistics associates to the selected band and the class
                Map<Range, Statistics[]> resultsPerRange = totalStatistics.get(band).get(classId);
                // Set of Ranges(if not used, it is only one object)
                Set<Range> ranges = resultsPerRange.keySet();

                for (Range r : ranges) {
                    // Selection of the statistic
                    Statistics stat = resultsPerRange.get(r)[0];
                    // Number of elements
                    Long countSimple = stat.getNumSamples();
                    // Addition of the result
                    statsMap.addResult(targetID, Double.valueOf(countSimple) * pixelArea, forceToZero);
                }
            }
        }

        @Override
        public void handleResults(ZoneGeometry result, ResultStatsMap statsMap, boolean forceToZero, int band) {

            // Default class value
            int classId = 0;
            // Total statistics
            Map<Integer, Map<Integer, Map<Range, Statistics[]>>> totalStatistics = result.getTotalStats();
            // Statistics associates to the selected band and the class
            Map<Range, Statistics[]> resultsPerRange = totalStatistics.get(band).get(classId);
            // Set of Ranges(if not used, it is only one object)
            Set<Range> ranges = resultsPerRange.keySet();

            for (Range r : ranges) {
                // Selection of the statistic
                Statistics stat = resultsPerRange.get(r)[0];
                // Number of elements
                Long countSimple = stat.getNumSamples();
                // Addition of the result
                statsMap.addResult(targetID, Double.valueOf(countSimple) * pixelArea, forceToZero);
            }
        }

    }

    /**
     * The GroundCoverageTarget is a special case of NotHumanTarget.
     * Basically in a single raster is stored more than one NotHuman target.
     * Depending on the value of the pixel it could refers to a target or another.
     * So before count the pixel we have to clusterize the results by the different pixel values 
     * 
     * @author DamianoG
     *
     */
    private static class GroundCoverageTarget extends TargetManager {

        /**
         * @param pixelArea
         */
        public GroundCoverageTarget(String targetID, Double[] distances, int pixelArea, Map targetCfg) {
            super(targetID, distances, pixelArea);
            pixelValues = new HashMap<Integer, String>();
            pixelValues.put((Integer) targetCfg.get("GROUPVALUE"), targetID);
            humanTarget = false;
        }

        @Override
        public void handleResults(List<Result> results, ResultStatsMap statsMap, boolean forceToZero) {
            for (Result el3 : results) {
                Double resSimple = (el3.getValue().equals(Double.NaN) ? 0d : el3.getValue());
                // Long countSimple = el3.getNumOffered();
                Iterator rangeIter = el3.getRanges().iterator();
                while (rangeIter.hasNext()) {
                    Range range = (Range) rangeIter.next();
                    Number value = range.getMax();
                    if (resSimple != 0 && resSimple != Double.NaN) {
                        statsMap.addResult(pixelValues.get(value.intValue()),
                                Double.valueOf(resSimple / value.doubleValue()) * pixelArea, forceToZero);
                    } else {
                        statsMap.addResult(pixelValues.get(value.intValue()), 0d, true);
                    }
                }
            }
        }

        @Override
        public void handleResults(ZoneGeometry result, ResultStatsMap statsMap, boolean forceToZero, int band) {
            return;
        }

    }

    /**
     * This method instantiate a concrete implementation of TargetManager parsing 
     * the provided Target name and init the targetID.
     * 
     * @param targetName
     * @param pixelArea if < 0 the default value is 100
     * 
     * @return a concrete implementation of TargetManager
     */
    public static TargetManager createManager(String targetId, TargetType type, Map vulnerabilityCfg,
            int pixelArea) {
        if (targetId == null) {
            throw new IllegalArgumentException("targetId is null...");
        }
        TargetManager manager = null;
        if (allDistances == null) {
            allDistances = VulnerabilityUtils.loadDistances(vulnerabilityCfg);
        }
        if (copSuoloValues == null) {
            copSuoloValues = VulnerabilityUtils.loadGroupValues(0, vulnerabilityCfg);
        }
        if (type == TargetType.COMPUTEPIXEL) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("HumanTarget Instantiated...");
            }
            manager = new HumanTarget(targetId, allDistances.get(targetId), pixelArea);
        } else if (type == TargetType.GROUPED) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("GroundCoverage Instantiated...");
            }
            manager = new GroundCoverageTarget(targetId, allDistances.get(targetId), pixelArea,
                    (Map) vulnerabilityCfg.get(Integer.parseInt(targetId)));
        } else if (type == TargetType.COMPUTEAREA) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("NotHumanTarget Instantiated...");
            }
            manager = new NotHumanTarget(targetId, allDistances.get(targetId), pixelArea);
        } else {
            throw new IllegalArgumentException(
                    targetId + " is not a supported target, check the properties file...");
        }

        return manager;
    }
}