package org.activityinfo.server.report.generator.map; /* * #%L * ActivityInfo Server * %% * Copyright (C) 2009 - 2013 UNICEF * %% * 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/gpl-3.0.html>. * #L% */ import org.activityinfo.model.type.geo.AiLatLng; import org.activityinfo.legacy.shared.model.SiteDTO; import org.activityinfo.legacy.shared.reports.content.*; import org.activityinfo.legacy.shared.reports.model.MapSymbol; import org.activityinfo.legacy.shared.reports.model.PointValue; import org.activityinfo.legacy.shared.reports.model.layers.PiechartMapLayer; import org.activityinfo.legacy.shared.reports.model.layers.PiechartMapLayer.Slice; import org.activityinfo.legacy.shared.reports.model.layers.ScalingType; import org.activityinfo.legacy.shared.reports.util.mapping.Extents; import org.activityinfo.server.database.hibernate.entity.Indicator; import org.activityinfo.server.report.generator.map.cluster.Cluster; import org.activityinfo.server.report.generator.map.cluster.Clusterer; import org.activityinfo.server.report.generator.map.cluster.ClustererFactory; import org.activityinfo.server.report.generator.map.cluster.genetic.MarkerGraph; import java.util.*; public class PiechartLayerGenerator extends PointLayerGenerator<PiechartMapLayer> { public PiechartLayerGenerator(PiechartMapLayer layer, Map<Integer, Indicator> indicators) { super(layer, indicators); } @Override public Extents calculateExtents() { // PRE---PASS - calculate extents of sites WITH non-zero // values for this indicator Extents extents = Extents.emptyExtents(); for (SiteDTO site : sites) { if (site.hasLatLong() && hasValue(site, layer.getIndicatorIds())) { extents.grow(site.getLatitude(), site.getLongitude()); } } return extents; } @Override public void generate(TiledMap map, MapContent content) { // create the list of input point values List<PointValue> points = new ArrayList<PointValue>(); List<PointValue> unmapped = new ArrayList<PointValue>(); // define our symbol scaling RadiiCalculator radiiCalculator; if (layer.getScaling() == ScalingType.None || layer.getMinRadius() == layer.getMaxRadius()) { radiiCalculator = new FixedRadiiCalculator(layer.getMinRadius()); } else if (layer.getScaling() == ScalingType.Graduated) { radiiCalculator = new GsLogCalculator(layer.getMinRadius(), layer.getMaxRadius()); } else { radiiCalculator = new FixedRadiiCalculator(layer.getMinRadius()); } Clusterer clusterer = ClustererFactory.fromClustering(layer.getClustering(), radiiCalculator, new BubbleIntersectionCalculator(layer.getMaxRadius())); generatePoints(map, layer, clusterer, points, unmapped); // add unmapped sites for (PointValue pv : unmapped) { content.getUnmappedSites().add(pv.getSite().getId()); } List<Cluster> clusters = clusterer.cluster(map, points); // create the markers List<BubbleMapMarker> markers = new ArrayList<BubbleMapMarker>(); for (Cluster cluster : clusters) { Point px = cluster.getPoint(); AiLatLng latlng = map.fromPixelToLatLng(px); BubbleMapMarker marker = new PieMapMarker(); sumSlices((PieMapMarker) marker, cluster.getPointValues()); for (PointValue pv : cluster.getPointValues()) { marker.getSiteIds().add(pv.getSite().getId()); } marker.setX(px.getX()); marker.setY(px.getY()); marker.setValue(cluster.sumValues()); marker.setRadius((int) cluster.getRadius()); marker.setLat(latlng.getLat()); marker.setLng(latlng.getLng()); marker.setAlpha(layer.getAlpha()); marker.setIndicatorIds(new HashSet<Integer>(layer.getIndicatorIds())); marker.setClusterAmount(cluster.getPointValues().size()); marker.setClustering(layer.getClustering()); markers.add(marker); } // number markers if applicable if (layer.getLabelSequence() != null) { numberMarkers(markers); } PieChartLegend legend = new PieChartLegend(); legend.setDefinition(layer); content.getMarkers().addAll(markers); content.addLegend(legend); } public void generatePoints(TiledMap map, PiechartMapLayer layer, Clusterer clusterer, List<PointValue> mapped, List<PointValue> unmapped) { // TODO: rework method for piechart (copy/pasted from bubblelayer) for (SiteDTO site : sites) { if (hasValue(site, layer.getIndicatorIds())) { Point px = null; if (site.hasLatLong()) { px = map.fromLatLngToPixel(new AiLatLng(site.getLatitude(), site.getLongitude())); } Double value = getValue(site, layer.getIndicatorIds()); if (value != null && value != 0) { PointValue pv = new PointValue(site, new MapSymbol(), value, px); calulateSlices(pv, site); if (clusterer.isMapped(site)) { mapped.add(pv); } else { unmapped.add(pv); } } } } } public int findColor(MapSymbol symbol, PiechartMapLayer layer) { return 0; } private void numberMarkers(List<BubbleMapMarker> markers) { // sort the markers, left-to right, top to bottom so the label // sequence is spatially consistent Collections.sort(markers, new MapMarker.LRTBComparator()); // add the labels for (BubbleMapMarker marker : markers) { marker.setLabel(layer.getLabelSequence().next()); } } public static class IntersectionCalculator implements MarkerGraph.IntersectionCalculator { private int radius; public IntersectionCalculator(int radius) { this.radius = radius; } @Override public boolean intersects(MarkerGraph.Node a, MarkerGraph.Node b) { return a.getPoint().distance(b.getPoint()) < radius * 2 && a.getPointValue().getSymbol().equals(b.getPointValue().getSymbol()); } } private void calulateSlices(PointValue pv, SiteDTO site) { pv.setSlices(new ArrayList<PieMapMarker.SliceValue>()); for (Slice slice : layer.getSlices()) { EntityCategory indicatorCategory = new EntityCategory(slice.getIndicatorId()); Double value = site.getIndicatorDoubleValue(slice.getIndicatorId()); if (value != null && value != 0) { PieMapMarker.SliceValue sliceValue = new PieMapMarker.SliceValue(); sliceValue.setValue(value); sliceValue.setCategory(indicatorCategory); sliceValue.setColor(slice.getColor()); sliceValue.setIndicatorId(slice.getIndicatorId()); pv.getSlices().add(sliceValue); } } } private void sumSlices(PieMapMarker marker, List<PointValue> pvs) { Map<DimensionCategory, PieMapMarker.SliceValue> slices = new HashMap<DimensionCategory, PieMapMarker.SliceValue>(); for (PointValue pv : pvs) { for (PieMapMarker.SliceValue slice : pv.getSlices()) { PieMapMarker.SliceValue summedSlice = slices.get(slice.getCategory()); if (summedSlice == null) { summedSlice = new PieMapMarker.SliceValue(slice); summedSlice.setIndicatorId(slice.getIndicatorId()); slices.put(slice.getCategory(), summedSlice); } else { summedSlice.setValue(summedSlice.getValue() + slice.getValue()); } } } marker.setSlices(new ArrayList<PieMapMarker.SliceValue>(slices.values())); } @Override public Margins calculateMargins() { return new Margins(layer.getMaxRadius()); } }