blob: 603ba0e974938acafe7adda3c48106db33b0d5ca [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.internal.feature;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
import java.time.Instant;
import org.opengis.util.LocalName;
import org.apache.sis.util.iso.Names;
import org.apache.sis.math.Vector;
import org.apache.sis.feature.DefaultAttributeType;
import org.apache.sis.referencing.crs.DefaultTemporalCRS;
import org.apache.sis.internal.util.UnmodifiableArrayList;
// Branch-dependent imports
import org.opengis.feature.Attribute;
import org.opengis.feature.AttributeType;
/**
* Utility methods for instantiating features where the geometry is a trajectory
* and some property values may change with time.
*
* This class is <strong>not</strong> thread-safe.
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.1
* @since 0.8
* @module
*/
public class MovingFeatures {
/**
* Definition of characteristics containing a list of instants, without duplicates.
* Should be in chronological order, but this is not verified.
*/
public static final AttributeType<Instant> TIME_AS_INSTANTS;
/**
* An alternative to {@link #TIME_AS_INSTANTS} used when times can not be mapped to calendar dates.
* This characteristic uses the same name than {@code TIME_AS_INSTANTS}. Consequently at most one
* of {@code TIME_AS_INSTANTS} and {@code TIME_AS_NUMBERS} can be used on the same property.
*/
private static final AttributeType<Number> TIME_AS_NUMBERS;
static {
final LocalName scope = Names.createLocalName("OGC", null, "MF");
final Map<String,Object> properties = Collections.singletonMap(
DefaultAttributeType.NAME_KEY, Names.createScopedName(scope, null, "datetimes"));
TIME_AS_INSTANTS = new DefaultAttributeType<>(properties, Instant.class, 0, Integer.MAX_VALUE, null);
TIME_AS_NUMBERS = new DefaultAttributeType<>(properties, Number.class, 0, Integer.MAX_VALUE, null);
}
/**
* Returns the "datetimes" characteristic to add on an attribute type.
* The characteristic will expect either {@link Instant} or {@link Number} values,
* depending on whether a temporal CRS is available or not.
*
* @param hasCRS whether a temporal CRS is available.
* @return the "datetimes" characteristic.
*/
public static AttributeType<?> characteristic(final boolean hasCRS) {
return hasCRS ? TIME_AS_INSTANTS : TIME_AS_NUMBERS;
}
/**
* Caches of list of times or instants, used for sharing existing instances.
* We do this sharing because it is common to have many properties having the same time characteristics.
*/
private final Map<Vector,InstantList> cache;
/**
* Creates a new builder.
*
* @param share other builder that may share time vectors, or {@code null} if none.
*/
public MovingFeatures(final MovingFeatures share) {
cache = (share != null) ? share.cache : new HashMap<>();
}
/**
* Sets the "datetimes" characteristic on the given attribute as a list of {@link Instant} instances.
* Should be in chronological order, but this is not verified.
*
* @param dest the attribute on which to set time characteristic.
* @param millis times in milliseconds since the epoch.
*/
public final void setInstants(final Attribute<?> dest, final long[] millis) {
final Attribute<Instant> c = TIME_AS_INSTANTS.newInstance();
c.setValues(cache.computeIfAbsent(InstantList.vectorize(millis), InstantList::new));
dest.characteristics().values().add(c);
}
/**
* Sets the "datetimes" characteristic on the given attribute.
* If the {@code converter} is non-null, it will be used for converting values to {@link Instant} instances.
* Otherwise values are stored as-is as time elapsed in arbitrary units since an arbitrary epoch.
*
* <p>Values should be in chronological order, but this is not verified.
* Current implementation does not cache the values, but this policy may be revisited in a future version.</p>
*
* @param dest the attribute on which to set time characteristic.
* @param values times in arbitrary units since an arbitrary epoch.
* @param converter the CRS to use for converting values to {@link Instant} instances, or {@code null}.
*/
public static void setTimes(final Attribute<?> dest, final Vector values, final DefaultTemporalCRS converter) {
final Attribute<?> ct;
if (converter != null) {
final Instant[] instants = new Instant[values.size()];
for (int i=0; i<instants.length; i++) {
instants[i] = converter.toInstant(values.doubleValue(i));
}
final Attribute<Instant> c = TIME_AS_INSTANTS.newInstance();
c.setValues(UnmodifiableArrayList.wrap(instants));
ct = c;
} else {
final Attribute<Number> c = TIME_AS_NUMBERS.newInstance();
c.setValues(values);
ct = c;
}
dest.characteristics().values().add(ct);
}
}