package org.activityinfo.server.report.generator; /* * #%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 com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.inject.Inject; import org.activityinfo.model.type.geo.AiLatLng; import org.activityinfo.legacy.shared.command.DimensionType; import org.activityinfo.legacy.shared.command.Filter; import org.activityinfo.legacy.shared.command.GetBaseMaps; import org.activityinfo.legacy.shared.command.result.BaseMapResult; import org.activityinfo.legacy.shared.model.BaseMap; import org.activityinfo.legacy.shared.model.IndicatorDTO; import org.activityinfo.legacy.shared.model.TileBaseMap; import org.activityinfo.legacy.shared.reports.content.GoogleBaseMap; import org.activityinfo.legacy.shared.reports.content.MapContent; import org.activityinfo.legacy.shared.reports.content.PredefinedBaseMaps; import org.activityinfo.legacy.shared.reports.model.DateRange; import org.activityinfo.legacy.shared.reports.model.MapReportElement; import org.activityinfo.legacy.shared.reports.model.layers.*; import org.activityinfo.legacy.shared.reports.util.mapping.Extents; import org.activityinfo.legacy.shared.reports.util.mapping.TileMath; import org.activityinfo.model.form.FormFieldType; import org.activityinfo.server.command.DispatcherSync; import org.activityinfo.server.database.hibernate.dao.IndicatorDAO; import org.activityinfo.server.database.hibernate.entity.Country; import org.activityinfo.server.database.hibernate.entity.Indicator; import org.activityinfo.server.database.hibernate.entity.User; import org.activityinfo.server.report.generator.map.*; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; /** * @author Alex Bertram */ public class MapGenerator extends ListGenerator<MapReportElement> { private final IndicatorDAO indicatorDAO; private static final Logger LOGGER = Logger.getLogger(MapGenerator.class.getName()); @Inject public MapGenerator(DispatcherSync dispatcher, IndicatorDAO indicatorDAO) { super(dispatcher); this.indicatorDAO = indicatorDAO; } @Override public void generate(User user, MapReportElement element, Filter inheritedFilter, DateRange dateRange) { Filter filter = GeneratorUtils.resolveElementFilter(element, dateRange); Filter effectiveFilter = inheritedFilter == null ? filter : new Filter(inheritedFilter, filter); MapContent content = new MapContent(); content.setFilterDescriptions(generateFilterDescriptions(filter, Collections.<DimensionType>emptySet(), user)); Map<Integer, Indicator> indicators = queryIndicators(element); // Set up layer generators List<LayerGenerator> layerGenerators = new ArrayList<LayerGenerator>(); for (MapLayer layer : element.getLayers()) { if (layer.isVisible()) { LayerGenerator layerGtor = createGenerator(layer, indicators); layerGtor.query(getDispatcher(), effectiveFilter); layerGenerators.add(layerGtor); } } // FIRST PASS: calculate extents and margins int width = element.getWidth(); int height = element.getHeight(); AiLatLng center; int zoom; Extents extents = Extents.emptyExtents(); Margins margins = new Margins(0); for (LayerGenerator layerGtor : layerGenerators) { extents.grow(layerGtor.calculateExtents()); margins.grow(layerGtor.calculateMargins()); } content.setExtents(extents); if (element.getCenter() == null) { // Now we're ready to calculate the zoom level // and the projection zoom = TileMath.zoomLevelForExtents(extents, width, height); center = extents.center(); } else { center = element.getCenter(); zoom = element.getZoomLevel(); } content.setCenter(center); // Retrieve the basemap and clamp zoom level BaseMap baseMap = findBaseMap(element, indicators.values()); if (zoom < baseMap.getMinZoom()) { zoom = baseMap.getMinZoom(); } if (zoom > baseMap.getMaxZoom()) { zoom = baseMap.getMaxZoom(); } if (zoom > element.getMaximumZoomLevel()) { zoom = element.getMaximumZoomLevel(); } TiledMap map = new TiledMap(width, height, center, zoom); content.setBaseMap(baseMap); content.setZoomLevel(zoom); // Generate the actual content for (LayerGenerator layerGtor : layerGenerators) { layerGtor.generate(map, content); } content.setIndicators(toDTOs(indicators.values())); element.setContent(content); } private LayerGenerator createGenerator(MapLayer layer, Map<Integer, Indicator> indicators) { if (layer instanceof BubbleMapLayer) { return new BubbleLayerGenerator((BubbleMapLayer) layer, indicators); } else if (layer instanceof IconMapLayer) { return new IconLayerGenerator((IconMapLayer) layer, indicators); } else if (layer instanceof PiechartMapLayer) { return new PiechartLayerGenerator((PiechartMapLayer) layer, indicators); } else if (layer instanceof PolygonMapLayer) { return new PolygonLayerGenerator((PolygonMapLayer) layer); } else { throw new UnsupportedOperationException(); } } private Map<Integer, Indicator> queryIndicators(MapReportElement element) { // Get relevant indicators for the map markers Set<Integer> indicatorIds = new HashSet<Integer>(); for (MapLayer maplayer : element.getLayers()) { indicatorIds.addAll(maplayer.getIndicatorIds()); } Map<Integer, Indicator> indicators = Maps.newHashMap(); for (Integer indicatorId : indicatorIds) { indicators.put(indicatorId, indicatorDAO.findById(indicatorId)); } return indicators; } private Set<IndicatorDTO> toDTOs(Collection<Indicator> indicators) { Set<IndicatorDTO> indicatorDTOs = new HashSet<IndicatorDTO>(); for (Indicator indicator : indicators) { IndicatorDTO indicatorDTO = new IndicatorDTO(); indicatorDTO.setId(indicator.getId()); indicatorDTO.setName(indicator.getName()); indicatorDTO.setType(FormFieldType.valueOf(indicator.getType())); indicatorDTO.setExpression(indicator.getExpression()); indicatorDTO.setSkipExpression(indicator.getSkipExpression()); indicatorDTOs.add(indicatorDTO); } return indicatorDTOs; } private BaseMap findBaseMap(MapReportElement element, Collection<Indicator> indicators) { BaseMap baseMap = null; String baseMapId = element.getBaseMapId(); if (element.getBaseMapId() == null || element.getBaseMapId().equals(MapReportElement.AUTO_BASEMAP)) { baseMapId = defaultBaseMap(indicators); } if (PredefinedBaseMaps.isPredefinedMap(baseMapId)) { baseMap = PredefinedBaseMaps.forId(baseMapId); } else { baseMap = getBaseMap(baseMapId); } return baseMap; } private String defaultBaseMap(Collection<Indicator> indicators) { Set<Country> countries = queryCountries(indicators); if (countries.size() == 1) { Country country = countries.iterator().next(); if ("CD".equals(country.getCodeISO())) { return "admin"; } } return GoogleBaseMap.ROADMAP.getId(); } private Set<Country> queryCountries(Collection<Indicator> indicators) { Set<Country> country = Sets.newHashSet(); for (Indicator indicator : indicators) { country.add(indicator.getActivity().getDatabase().getCountry()); } return country; } private BaseMap getBaseMap(String baseMapId) { BaseMapResult maps = dispatcher.execute(new GetBaseMaps()); for (TileBaseMap map : maps.getBaseMaps()) { if (map.getId().equals(baseMapId)) { return map; } } LOGGER.log(Level.SEVERE, "Could not find base map id=" + baseMapId); return TileBaseMap.createNullMap(baseMapId); } }