org.mapfish.print.attribute.map.MapAttribute Maven / Gradle / Ivy
Show all versions of print-lib Show documentation
package org.mapfish.print.attribute.map;
import com.google.common.base.Function;
import com.vividsolutions.jts.geom.Envelope;
import org.json.JSONArray;
import org.mapfish.print.attribute.map.OverviewMapAttribute.OverviewMapAttributeValues;
import org.mapfish.print.attribute.map.ZoomToFeatures.ZoomType;
import org.mapfish.print.config.Template;
import org.mapfish.print.parser.CanSatisfyOneOf;
import org.mapfish.print.parser.HasDefaultValue;
import org.mapfish.print.parser.OneOf;
import org.mapfish.print.parser.Requires;
import org.mapfish.print.wrapper.PArray;
import org.mapfish.print.wrapper.json.PJsonArray;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
/**
* The attributes for {@link org.mapfish.print.processor.map.CreateMapProcessor} (see
* !createMap processor).
* [[examples=verboseExample]]
*/
public final class MapAttribute extends GenericMapAttribute {
private static final double DEFAULT_SNAP_TOLERANCE = 0.05;
private static final ZoomLevelSnapStrategy DEFAULT_SNAP_STRATEGY =
ZoomLevelSnapStrategy.CLOSEST_LOWER_SCALE_ON_TIE;
private static final boolean DEFAULT_SNAP_GEODETIC = false;
@SuppressWarnings("unchecked")
@Override
public Class getValueType() {
return MapAttributeValues.class;
}
@Override
public MapAttributeValues createValue(final Template template) {
return new MapAttributeValues(template, getWidth(), getHeight());
}
/**
* The value of {@link MapAttribute}.
*/
public class MapAttributeValues extends GenericMapAttribute.GenericMapAttributeValues {
private static final boolean DEFAULT_ADJUST_BOUNDS = false;
private static final double DEFAULT_ROTATION = 0.0;
/**
* An array of 4 doubles, minX, minY, maxX, maxY. The bounding box of the map.
*
* Either the bbox or the center + scale must be defined
*
*/
@OneOf("MapBounds")
public double[] bbox;
/**
* A GeoJSON geometry that is essentially the area of the area to draw on the map.
*
*/
@CanSatisfyOneOf("MapBounds")
@HasDefaultValue
public AreaOfInterest areaOfInterest;
/**
* An array of 2 doubles, (x, y). The center of the map.
*/
@Requires("scale")
@OneOf("MapBounds")
public double[] center;
/**
* If center is defined then this is the scale of the map centered at center.
*/
@HasDefaultValue
public Double scale;
/**
* Zoom the map to the features of a specific layer or all features of the map.
*/
@OneOf("MapBounds")
public ZoomToFeatures zoomToFeatures;
/**
* The json with all the layer information. This will be parsed in postConstruct into a list of
* layers and therefore this field should not normally be accessed.
*
* The first layer in the array will be the top layer in the map. The last layer in the array will be
* the bottom layer in the map. There for the last layer will be hidden by the first layer (where not
* transparent).
*/
@HasDefaultValue
public PArray layers = new PJsonArray(null, new JSONArray(), null);
/**
* The output dpi of the printed map.
*/
public double dpi;
private MapBounds mapBounds;
/**
* Constructor.
*
* @param template the template this map is part of.
* @param width the width of the map.
* @param height the height of the map.
*/
public MapAttributeValues(final Template template, final Integer width, final Integer height) {
super(template, width, height);
}
/**
* Constructor.
*
* @param template the template this map is part of.
*/
public MapAttributeValues(final Template template) {
super(template);
}
@Override
public Double getDpi() {
return this.dpi;
}
@Override
public final PArray getRawLayers() {
return this.layers;
}
@Override
public void setRawLayers(final PArray newLayers) {
this.layers = newLayers;
}
@Override
public final void postConstruct() throws FactoryException {
super.postConstruct();
if (getDpi() > getMaxDpi()) {
throw new IllegalArgumentException(
"dpi parameter was " + getDpi() + " must be limited to " + getMaxDpi()
+ ". The path to the parameter is: " + getDpi());
}
if (this.zoomToFeatures != null) {
if (this.zoomToFeatures.zoomType == ZoomType.CENTER) {
if (this.scale == null && this.zoomToFeatures.minScale == null) {
throw new IllegalArgumentException(
"When using 'zoomToFeatures.zoom.Type: center' either 'scale' " +
"or 'zoomToFeatures.minScale' has to be given.");
}
} else if (this.zoomToFeatures.zoomType == ZoomType.EXTENT
&& this.zoomToFeatures.minScale == null) {
throw new IllegalArgumentException(
"When using 'zoomToFeatures.zoom.Type: extent' 'zoomToFeatures.minScale'" +
"has to be given.");
}
}
this.mapBounds = parseBounds();
}
private MapBounds parseBounds() {
final CoordinateReferenceSystem crs = parseProjection();
if (this.center != null && this.bbox != null) {
throw new IllegalArgumentException("Cannot have both center and bbox defined");
}
MapBounds bounds;
if (this.center != null) {
double centerX = this.center[0];
double centerY = this.center[1];
bounds = new CenterScaleMapBounds(crs, centerX, centerY, this.scale);
} else if (this.bbox != null) {
final int maxYIndex = 3;
double minX = this.bbox[0];
double minY = this.bbox[1];
double maxX = this.bbox[2];
double maxY = this.bbox[maxYIndex];
bounds = new BBoxMapBounds(crs, minX, minY, maxX, maxY);
} else if (this.areaOfInterest != null) {
Envelope area = this.areaOfInterest.getArea().getEnvelopeInternal();
bounds = new BBoxMapBounds(crs, area);
} else if (this.zoomToFeatures != null) {
bounds = new BBoxMapBounds(crs, 0, 0, 10, 10);
} else {
throw new IllegalArgumentException("Expected either: center and scale, bbox, or an " +
"areaOfInterest defined in order to calculate " +
"the map bounds");
}
return bounds;
}
public MapBounds getMapBounds() {
return this.mapBounds;
}
public void setMapBounds(final MapBounds mapBounds) {
this.mapBounds = mapBounds;
}
/**
* Recalculate the bounds after center or bounds have changed.
*/
public void recalculateBounds() {
this.mapBounds = parseBounds();
}
@Override
public String getProjection() {
return getValueOr(super.getProjection(), DEFAULT_PROJECTION);
}
@Override
public Double getZoomSnapTolerance() {
return getValueOr(super.getZoomSnapTolerance(), DEFAULT_SNAP_TOLERANCE);
}
@Override
public ZoomLevelSnapStrategy getZoomLevelSnapStrategy() {
return getValueOr(super.getZoomLevelSnapStrategy(), DEFAULT_SNAP_STRATEGY);
}
@Override
public Boolean getZoomSnapGeodetic() {
return getValueOr(super.getZoomSnapGeodetic(), DEFAULT_SNAP_GEODETIC);
}
@Override
public Double getRotation() {
return getValueOr(super.getRotation(), DEFAULT_ROTATION);
}
@Override
public Boolean isUseNearestScale() {
return (this.useNearestScale == null || this.useNearestScale)
&& getZoomLevels() != null;
}
@Override
public Boolean isUseAdjustBounds() {
return getValueOr(super.isUseAdjustBounds(), DEFAULT_ADJUST_BOUNDS);
}
/**
* Creates an {@link org.mapfish.print.attribute.map.MapAttribute.OverriddenMapAttributeValues}
* instance with the current object and a given
* {@link org.mapfish.print.attribute.map.OverviewMapAttribute.OverviewMapAttributeValues}
* instance.
*
* @param paramOverrides Attributes set in this instance will override attributes in the
* current instance.
*/
public final OverriddenMapAttributeValues getWithOverrides(
final OverviewMapAttributeValues paramOverrides) {
return new OverriddenMapAttributeValues(this, paramOverrides, getTemplate());
}
/**
* Create a copy of this instance. Should be overridden for each subclass.
*
* @param width the width of the new map attribute values to create
* @param height the height of the new map attribute values to create
* @param updater a function which will be called after copy is made but before postConstruct
* is called in order to do other configuration changes.
*/
public MapAttribute.MapAttributeValues copy(
final int width, final int height,
@Nonnull final Function updater) {
MapAttributeValues copy = new MapAttributeValues(getTemplate(), width, height);
copy.areaOfInterest = this.areaOfInterest.copy();
copy.bbox = this.bbox;
copy.center = this.center;
copy.scale = this.scale;
copy.layers = this.layers;
copy.dpi = this.getDpi();
copy.projection = this.getProjection();
copy.rotation = this.getRotation();
copy.useNearestScale = this.isUseNearestScale();
copy.useAdjustBounds = this.useAdjustBounds;
copy.longitudeFirst = this.longitudeFirst;
copy.zoomToFeatures = (this.zoomToFeatures == null) ? null : this.zoomToFeatures.copy();
updater.apply(copy);
try {
copy.postConstruct();
} catch (FactoryException e) {
throw new RuntimeException(e);
}
return copy;
}
}
/**
* A wrapper around a {@link MapAttributeValues} instance and an
* {@link org.mapfish.print.attribute.map.OverviewMapAttribute.OverviewMapAttributeValues}
* instance, which is used to render the overview map.
*
* If attributes on the
* {@link org.mapfish.print.attribute.map.OverviewMapAttribute.OverviewMapAttributeValues}
* instance are set, those attributes will be returned, otherwise the ones on {@link MapAttributeValues}.
*/
public class OverriddenMapAttributeValues extends MapAttributeValues {
private final MapAttributeValues params;
private final OverviewMapAttributeValues paramOverrides;
private MapBounds zoomedOutBounds = null;
private MapLayer mapExtentLayer = null;
/**
* Constructor.
*
* @param params The fallback parameters.
* @param paramOverrides The parameters explicitly defined for the overview map.
* @param template The template this map is part of.
*/
public OverriddenMapAttributeValues(
final MapAttributeValues params,
final OverviewMapAttributeValues paramOverrides,
final Template template) {
super(template, paramOverrides.getWidth(), paramOverrides.getHeight());
this.params = params;
this.paramOverrides = paramOverrides;
}
/**
* The bounds used to render the overview-map.
*/
@Override
public final MapBounds getMapBounds() {
return this.zoomedOutBounds;
}
/**
* Custom bounds for the overview-map. Overwrites the bounds of the original map.
*/
public final MapBounds getCustomBounds() {
return this.paramOverrides.getMapBounds();
}
/**
* The bounds of the original map that this overview-map is associated to.
*/
public final MapBounds getOriginalBounds() {
return this.params.getMapBounds();
}
public final void setZoomedOutBounds(final MapBounds zoomedOutBounds) {
this.zoomedOutBounds = zoomedOutBounds;
}
@Override
public final Double getDpi() {
return getValueOr(this.paramOverrides.getDpi(), this.params.getDpi());
}
@Override
public final Double getZoomSnapTolerance() {
return getValueOr(this.paramOverrides.getZoomSnapTolerance(), this.params.getZoomSnapTolerance());
}
@Override
public final ZoomLevelSnapStrategy getZoomLevelSnapStrategy() {
return getValueOr(this.paramOverrides.getZoomLevelSnapStrategy(),
this.params.getZoomLevelSnapStrategy());
}
@Override
public final ZoomLevels getZoomLevels() {
return getValueOr(this.paramOverrides.getZoomLevels(), this.params.getZoomLevels());
}
@Override
public final Double getRotation() {
return getValueOr(this.paramOverrides.getRotation(), this.params.getRotation());
}
@Override
public final Boolean isUseAdjustBounds() {
return getValueOr(this.paramOverrides.isUseAdjustBounds(), this.params.isUseAdjustBounds());
}
@Override
public final Boolean isUseNearestScale() {
final Boolean useNearestScale =
getValueOr(this.paramOverrides.useNearestScale, this.params.useNearestScale);
return (useNearestScale == null || useNearestScale)
&& getZoomLevels() != null;
}
public final void setMapExtentLayer(final MapLayer mapExtentLayer) {
this.mapExtentLayer = mapExtentLayer;
}
@Override
public final List getLayers() {
// return the layers together with a layer for the bbox rectangle of the map
List layers = new ArrayList<>();
if (this.mapExtentLayer != null) {
layers.add(this.mapExtentLayer);
}
if (!this.paramOverrides.getLayers().isEmpty()) {
layers.addAll(this.paramOverrides.getLayers());
} else {
layers.addAll(this.params.getLayers());
}
return layers;
}
}
}