All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geomajas.internal.configuration.ConfigurationDtoPostProcessor Maven / Gradle / Ivy

/*
 * This file is part of Geomajas, a component framework for building
 * rich Internet applications (RIA) with sophisticated capabilities for the
 * display, analysis and management of geographic information.
 * It is a building block that allows developers to add maps
 * and other geographic data capabilities to their web applications.
 *
 * Copyright 2008-2010 Geosparc, http://www.geosparc.com, Belgium
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 */
package org.geomajas.internal.configuration;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.geomajas.configuration.FeatureStyleInfo;
import org.geomajas.configuration.LayerInfo;
import org.geomajas.configuration.NamedStyleInfo;
import org.geomajas.configuration.RasterLayerInfo;
import org.geomajas.configuration.VectorLayerInfo;
import org.geomajas.configuration.client.ClientApplicationInfo;
import org.geomajas.configuration.client.ClientLayerInfo;
import org.geomajas.configuration.client.ClientMapInfo;
import org.geomajas.configuration.client.ClientVectorLayerInfo;
import org.geomajas.configuration.client.ScaleInfo;
import org.geomajas.geometry.Bbox;
import org.geomajas.geometry.Coordinate;
import org.geomajas.global.ExceptionCode;
import org.geomajas.global.GeomajasException;
import org.geomajas.layer.Layer;
import org.geomajas.layer.LayerException;
import org.geomajas.layer.RasterLayer;
import org.geomajas.service.DtoConverterService;
import org.geomajas.service.GeoService;
import org.geotools.geometry.DirectPosition2D;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.GeodeticCalculator;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.vividsolutions.jts.geom.Envelope;

/**
 * Post-processes configuration DTOs. Generally responsible for any behaviour that would violate the DTO contract
 * (especially for GWT) if it would be added to the configuration objects themselves, such as hooking up client
 * configurations to their server layers.
 * 
 * @author Jan De Moerloose
 */
@Component
public class ConfigurationDtoPostProcessor {

	private static final double METER_PER_INCH = 0.0254;

	private final Logger log = LoggerFactory.getLogger(ConfigurationDtoPostProcessor.class);

	@Autowired
	private DtoConverterService converterService;

	@Autowired
	private GeoService geoService;

	@Autowired(required = false)
	protected Map applicationMap = new LinkedHashMap();

	@Autowired(required = false)
	protected Map namedStyleMap = new LinkedHashMap();

	@Autowired(required = false)
	protected Map> layerMap = new LinkedHashMap>();

	@Autowired(required = false)
	protected Map rasterLayerMap = new LinkedHashMap();

	public ConfigurationDtoPostProcessor() {

	}

	@PostConstruct
	public void processConfiguration() throws BeansException {
		try {
			for (RasterLayer layer : rasterLayerMap.values()) {
				postProcess(layer);
			}
			for (ClientApplicationInfo application : applicationMap.values()) {
				postProcess(application);
			}
			for (NamedStyleInfo style : namedStyleMap.values()) {
				postProcess(style);
			}
		} catch (LayerException e) {
			throw new BeanInitializationException("Could not post process configuration", e);
		}
	}

	private void postProcess(RasterLayer layer) throws LayerException {
		RasterLayerInfo info = layer.getLayerInfo();
		for (ScaleInfo scale : info.getZoomLevels()) {
			// for raster layers we don't accept 1 : x notation !
			if (scale.getDenominator() != 0) {
				throw new LayerException(ExceptionCode.CONVERSION_PROBLEM, "Raster layer " + layer.getId()
						+ " has zoom level " + scale.getNumerator() + " : " + scale.getDenominator()
						+ " in disallowed 1 : x notation");
			}
			// add the resolution for deprecated api support
			info.getResolutions().add(1. / scale.getPixelPerUnit());
		}
	}

	private ClientApplicationInfo postProcess(ClientApplicationInfo client) throws LayerException {
		// initialize maps
		for (ClientMapInfo map : client.getMaps()) {
			map.setUnitLength(getUnitLength(map.getCrs(), map.getInitialBounds()));
			// result should be m = (m/inch) / (number/inch)
			map.setPixelLength(METER_PER_INCH / client.getScreenDpi());
			log.debug("Map " + map.getId() + " has unit length : " + map.getUnitLength() + "m, pixel length "
					+ map.getPixelLength() + "m");
			// calculate scales
			double pixPerUnit = map.getUnitLength() / map.getPixelLength();
			// if resolutions have been defined the old way, calculate the scale configuration
			if (map.getResolutions().size() > 0) {
				for (Double resolution : map.getResolutions()) {
					if (map.isResolutionsRelative()) {
						map.getScaleConfiguration().getZoomLevels().add(new ScaleInfo(1., resolution));
					} else {
						map.getScaleConfiguration().getZoomLevels().add(new ScaleInfo(1. / resolution));
					}
				}
				map.getResolutions().clear();
			}
			// convert the scales so we have both relative and pix/unit
			boolean relativeScales = true;
			for (ScaleInfo scale : map.getScaleConfiguration().getZoomLevels()) {
				if (scale.getDenominator() == 0) {
					relativeScales = false;
				} else if (!relativeScales) {
					throw new LayerException(ExceptionCode.SCALE_CONVERSION_PROBLEM, map.getId());
				}
				completeScale(scale, pixPerUnit);
				// add the resolution for deprecated api support
				if (map.isResolutionsRelative()) {
					map.getResolutions().add(1. / scale.getPixelPerUnit());
				} else {
					map.getResolutions().add(scale.getDenominator() / scale.getNumerator());
				}
			}
			completeScale(map.getScaleConfiguration().getMaximumScale(), pixPerUnit);
			for (ClientLayerInfo layer : map.getLayers()) {
				String layerId = layer.getServerLayerId();
				Layer serverLayer = layerMap.get(layerId);
				if (serverLayer == null) {
					throw new LayerException(ExceptionCode.LAYER_NOT_FOUND, layerId);
				}
				LayerInfo layerInfo = serverLayer.getLayerInfo();
				layer.setLayerInfo(layerInfo);
				layer.setMaxExtent(getClientMaxExtent(map.getCrs(), layer.getCrs(), layerInfo.getMaxExtent(), layerId));
				completeScale(layer.getMaximumScale(), pixPerUnit);
				completeScale(layer.getMinimumScale(), pixPerUnit);
				log.debug("Layer " + layer.getId() + " has scale range : " + layer.getMinimumScale().getPixelPerUnit()
						+ "," + layer.getMaximumScale().getPixelPerUnit());
				if (layer instanceof ClientVectorLayerInfo) {
					postProcess((ClientVectorLayerInfo) layer);
				}
			}
		}
		return client;
	}

	private ClientVectorLayerInfo postProcess(ClientVectorLayerInfo layer) throws LayerException {
		// copy feature info from server if not explicitly defined
		if (layer.getFeatureInfo() == null) {
			VectorLayerInfo serverInfo = (VectorLayerInfo) layer.getLayerInfo();
			layer.setFeatureInfo(serverInfo.getFeatureInfo());
		}

		return layer;
	}

	private NamedStyleInfo postProcess(NamedStyleInfo client) {
		// index styles
		int i = 0;
		for (FeatureStyleInfo style : client.getFeatureStyles()) {
			style.setIndex(i++);
			style.setStyleId(client.getName() + "-" + style.getIndex());
		}
		return client;
	}

	private double getUnitLength(String mapCrsKey, Bbox mapBounds) throws LayerException {
		try {
			if (null == mapBounds) {
				throw new LayerException(ExceptionCode.MAP_MAX_EXTENT_MISSING);
			}
			CoordinateReferenceSystem crs = geoService.getCrs(mapCrsKey);
			GeodeticCalculator calculator = new GeodeticCalculator(crs);
			Coordinate center = new Coordinate(0.5 * (mapBounds.getX() + mapBounds.getMaxX()),
					0.5 * (mapBounds.getY() + mapBounds.getMaxY()));
			calculator.setStartingPosition(new DirectPosition2D(crs, center.getX(), center.getY()));
			calculator.setDestinationPosition(new DirectPosition2D(crs, center.getX() + 1, center.getY()));
			return calculator.getOrthodromicDistance();
		} catch (TransformException e) {
			throw new LayerException(e, ExceptionCode.TRANSFORMER_CREATE_LAYER_TO_MAP_FAILED);
		}
	}

	public Bbox getClientMaxExtent(String mapCrsKey, String layerCrsKey, Bbox serverBbox, String layer)
			throws LayerException {
		if (mapCrsKey.equals(layerCrsKey)) {
			return serverBbox;
		}
		try {
			CoordinateReferenceSystem mapCrs = geoService.getCrs(mapCrsKey);
			CoordinateReferenceSystem layerCrs = geoService.getCrs(layerCrsKey);
			Envelope serverEnvelope = converterService.toInternal(serverBbox);
			MathTransform transformer = geoService.findMathTransform(layerCrs, mapCrs);
			Bbox res = converterService.toDto(JTS.transform(serverEnvelope, transformer));
			if (Double.isNaN(res.getX()) || Double.isNaN(res.getY()) || Double.isNaN(res.getWidth())
					|| Double.isNaN(res.getHeight())) {
				throw new LayerException(ExceptionCode.LAYER_EXTENT_CANNOT_CONVERT, layer, mapCrsKey);
			}
			return res;
		} catch (TransformException e) {
			throw new LayerException(e, ExceptionCode.TRANSFORMER_CREATE_LAYER_TO_MAP_FAILED);
		} catch (GeomajasException e) {
			throw new LayerException(e, ExceptionCode.TRANSFORMER_CREATE_LAYER_TO_MAP_FAILED);
		}
	}

	/**
	 * Convert the scale in pixels per unit or relative values, which ever is missing.
	 *
	 * @param scaleInfo
	 *            scaleInfo object which needs to be completed
	 * @param mapUnitInPixels
	 *            the number of pixels in a map unit
	 */
	public void completeScale(ScaleInfo scaleInfo, double mapUnitInPixels) {
		if (0 == mapUnitInPixels) {
			throw new RuntimeException("ScaleInfo.completeScale mapUnitInPixels should never be zero.");
		}
		double denominator = scaleInfo.getDenominator();
		double numerator = scaleInfo.getNumerator();
		if (denominator != 0) {
			scaleInfo.setPixelPerUnit(numerator / denominator * mapUnitInPixels);
		} else {
			double pixelPerUnit = scaleInfo.getPixelPerUnit();
			if (pixelPerUnit > mapUnitInPixels) {
				scaleInfo.setNumerator(pixelPerUnit / mapUnitInPixels);
				scaleInfo.setDenominator(1);
			} else {
				scaleInfo.setNumerator(1);
				scaleInfo.setDenominator(mapUnitInPixels / pixelPerUnit);
			}
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy