blob: c99f33ce52fa9a34ccd0e81e496b2e4ee5435800 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sis.map;
import java.util.Objects;
import java.util.Optional;
import org.opengis.coverage.Coverage;
import org.opengis.feature.Feature;
import org.opengis.geometry.Envelope;
import org.apache.sis.storage.Aggregate;
import org.apache.sis.storage.DataSet;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.Query;
import org.apache.sis.storage.Resource;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.style.Style;
/**
* Data (resource) associated to rules for visual representation (symbology).
* Layers are the key elements of a map: they link data (given by {@link Resource}s) or a subset of
* those data (filtered by {@link Query}) to their visual representation (defined by {@link Style}s).
* The visual appearance of a layer should be similar with any rendering engine.
* Some details may very because of different rendering strategies for label placements, 2D or 3D,
* but the fundamentals aspect of each {@link Feature} or {@link Coverage} should be unchanged.
*
* @author Johann Sorel (Geomatys)
* @version 1.2
* @since 1.2
*/
public class MapLayer extends MapItem {
/**
* The {@value} property name, used for notifications about changes in map layer resource.
* The data resource provides the digital data to be rendered. Note that not all kinds of resources
* are digital data. For example, a resource may be a citation of facts or figures printed on paper,
* photographic material, or other media (see all {@link org.opengis.metadata.citation.PresentationForm}
* values having the {@code _HARDCOPY} suffix in their name).
* Associated values should be instances of {@link DataSet} or {@link Aggregate}.
*
* @see #getData()
* @see #setData(Resource)
*/
public static final String DATA_PROPERTY = "data";
/**
* The {@value} property name, used for notifications about changes in map layer query.
* The query can filter resource data for rendering only a subset of available data.
* Associated values are instances of {@link Query}.
*
* @see #getQuery()
* @see #setQuery(Query)
*/
public static final String QUERY_PROPERTY = "query";
/**
* The {@value} property name, used for notifications about changes in map layer style.
* The style specifies the appearance of the filtered data to be rendered.
* Associated values are instances of {@link Style}.
*
* @see #getStyle()
* @see #setStyle(Style)
*/
public static final String STYLE_PROPERTY = "style";
/**
* The {@value} property name, used for notifications about changes in map layer opacity.
* The opacity specifies the gloabal opacity of the data to be rendered.
*
* @see #getOpacity()
* @see #setOpacity(double)
*/
public static final String OPACITY_PROPERTY = "opacity";
/**
* Data to be rendered, or {@code null} if unavailable.
*
* @see #DATA_PROPERTY
* @see #getData()
*/
private Resource resource;
/**
* Filter for rendering a subset of available data, or {@code null} if none.
*
* @see #QUERY_PROPERTY
* @see #getQuery()
*/
private Query query;
/**
* Visual representation of data, or {@code null} if none.
*
* @see #STYLE_PROPERTY
* @see #getStyle()
*/
private Style style;
/**
* Visual transparency of data, or {@code null} if none.
*
* @see #OPACITY_PROPERTY
* @see #getOpacity()
*/
private double opacity = 1.0;
/**
* Constructs an initially empty map layer.
*
* @todo Expect {@code Resource} and {@code Style} in argument, for discouraging
* the use of {@code MapLayer} with null resource and null style?
*/
public MapLayer() {
}
/**
* Returns the data (resource) represented by this layer.
* The resource should be a {@link DataSet}, but {@link Aggregate} is also accepted.
* The behavior in aggregate case depends on the rendering engine.
*
* @return data to be rendered, or {@code null} is unavailable.
*
* @see #DATA_PROPERTY
*/
public Resource getData() {
return resource;
}
/**
* Sets the data (resource) to be rendered.
* The resource should never be null, still the null case is tolerated to indicate
* that the layer should have existed but is unavailable for an unspecified reason.
* This case may happen with processing or distant services resources.
*
* <p>The given resource should be a {@link DataSet} or an {@link Aggregate} of data sets.
* However, this base class does not enforce those types. Subclasses may restrict the set
* of resource types accepted by this method.</p>
*
* <p>Note that not all kinds of resources are digital data. For example, a resource may be an organization,
* or citation of facts, tables and figures printed on paper, photographic material, or other media
* (see all {@link org.opengis.metadata.citation.PresentationForm} values having the {@code _HARDCOPY}
* suffix in their name). The kind of resources in {@code MapLayer} shall be one of those representing
* digital data.</p>
*
* @param newValue the new data, or {@code null} if unavailable.
*/
public void setData(final Resource newValue) {
final Resource oldValue = resource;
if (!Objects.equals(oldValue, newValue)) {
resource = newValue;
firePropertyChange(DATA_PROPERTY, oldValue, newValue);
}
}
/**
* Returns the filter for reducing the amount of data to render. Query filters can be
* specified for rendering a smaller amount of data than what the resource can provide.
* If the query is undefined, then all data will be rendered.
*
* @return filter for reducing data, or {@code null} for rendering all data.
*
* @see #QUERY_PROPERTY
*/
public Query getQuery() {
return query;
}
/**
* Sets a filter for reducing the amount of data to render. If this method is never invoked, the default value
* is {@code null}. If the given value is different than the previous value, then a change event is sent to all
* listeners registered for the {@value #QUERY_PROPERTY} property.
*
* @param newValue filter for reducing data, or {@code null} for rendering all data.
*/
public void setQuery(final Query newValue) {
final Query oldValue = query;
if (!Objects.equals(oldValue, newValue)) {
query = newValue;
firePropertyChange(QUERY_PROPERTY, oldValue, newValue);
}
}
/**
* Returns the visual appearance of the data.
* If the style is undefined, the behavior is left to the rendering engine.
* It is expected that a default style should be used.
*
* @return description of data visual appearance, or {@code null} if unspecified.
*/
public Style getStyle() {
return style;
}
/**
* Sets the visual appearance of the data. If this method is never invoked, the default value is {@code null}.
* If the given value is different than the previous value, then a change event is sent to all listeners
* registered for the {@value #STYLE_PROPERTY} property.
*
* @param newValue description of data visual appearance, or {@code null} if unspecified.
*/
public void setStyle(final Style newValue) {
final Style oldValue = style;
if (!Objects.equals(oldValue, newValue)) {
style = newValue;
firePropertyChange(STYLE_PROPERTY, oldValue, newValue);
}
}
/**
* Returns the global opacity of this layer.
* Based on the rendering context this property may be impossible to implement,
* it is therefor recommended to modify the style symbolizer opacity properties.
*
* @return opacity between 0.0 and 1.0
*/
public double getOpacity() {
return opacity;
}
/**
* Sets the global rendering opacity of this layer.
*
* @param opacity must be betwen 0.0 and 1.0
*/
public void setOpacity(double opacity) {
ArgumentChecks.ensureBetween(OPACITY_PROPERTY, 0.0, 1.0, opacity);
if (this.opacity != opacity) {
double old = this.opacity;
this.opacity = opacity;
firePropertyChange(OPACITY_PROPERTY, old, opacity);
}
}
/**
* Returns the envelope of this {@code MapLayer}.
* The envelope is the resource data envelope.
*
* @return the spatiotemporal extent. May be absent if none or too costly to compute.
* @throws DataStoreException if an error occurred while reading or computing the envelope.
*/
@Override
public Optional<Envelope> getEnvelope() throws DataStoreException {
Resource data = getData();
if (data instanceof DataSet) {
return ((DataSet) data).getEnvelope();
}
return Optional.empty();
}
}