This tutorial explains how you can add more handles to an existing handles provider.
|
Other customizations
You need to customize a handles provider for advanced use cases only. For other customization methods, see this article. |
The code snippets in this tutorial are available in the editing sample.
See this article for more information about editing.
Step 1 - Add an extra handle to an existing IEditHandles
The best way to add an extra handle to a set of handles is to wrap an existing IEditHandlesIEditHandlesIEditHandles instance, and add the
handle. In this tutorial, we use two classes for this:
-
EditHandlesWrapper: thisIEditHandlesIEditHandlesIEditHandlesimplementation is a utility class that makes it easier to wrap otherIEditHandlesIEditHandlesIEditHandlesinstances. You can use it to override theIEditHandles::getListIEditHandles::getListIEditHandles::getListmethod to add more handles. Any change in the delegateIEditHandlesIEditHandlesIEditHandlesalso causes the wrapper to call theconfiguredconfiguredconfiguredobservers. -
CustomFeatureEditHandles: this class extends fromEditHandlesWrapper, and adds a handle that lets you change a 'transparency' property of the edited Feature.
The following code shows how you can do this. You can find the full code in the sample.
class CustomFeatureEditHandles final : public EditHandlesWrapper {
public:
explicit CustomFeatureEditHandles(std::shared_ptr<Observable<Feature>> feature,
std::shared_ptr<IFeatureEditCallback> featureEditCallback,
std::shared_ptr<IEditHandles> delegateHandles,
const std::shared_ptr<FeatureEditContext>& context)
: EditHandlesWrapper(std::move(delegateHandles)), _feature{std::move(feature)}, _featureEditCallback{std::move(featureEditCallback)} {
// Create a handle that controls the transparency
_transparencyHandle = createTransparencyHandle(context);
}
std::vector<std::shared_ptr<IEditHandle>> getList() const override {
auto handles = EditHandlesWrapper::getList();
if (_transparencyHandle) {
handles.emplace_back(_transparencyHandle);
}
return handles;
}
private:
std::shared_ptr<PointEditHandle> createTransparencyHandle(const std::shared_ptr<FeatureEditContext>& context) {
// Create a handle that changes the transparency when the handle is dragged
return handle;
}
};
internal sealed class CustomFeatureEditHandles : EditHandlesWrapper
{
private readonly Observable<Feature> _feature; // The current feature
private readonly IFeatureEditCallback _featureEditCallback; // Called by the transparency handle when changing the feature state
private Observable<Polyline> _polyline; // The current polyline. This is derived from _feature
private IInvalidationCallback _featureInvalidateCallback; // Used to detect feature changes, so that the handle location can be updated
private readonly PointEditHandle _transparencyHandle; // The handle used to edit the transparency
public CustomFeatureEditHandles(Observable<Feature> feature, IFeatureEditCallback featureEditCallback, IEditHandles delegateHandles, FeatureEditContext context) :
base(delegateHandles)
{
_feature = feature;
_featureEditCallback = featureEditCallback;
// Create a handle that controls the transparency
_transparencyHandle = CreateTransparencyHandle(context);
}
public override IList<IEditHandle> GetList()
{
var handles = base.GetList();
if (_transparencyHandle == null) return handles;
handles = new List<IEditHandle>(handles) { _transparencyHandle };
return handles;
}
private PointEditHandle CreateTransparencyHandle(FeatureEditContext context)
{
// Create a handle that changes the transparency when the handle is dragged
return handle;
}
}
private class CustomFeatureEditHandles(// The current feature
private val feature: Observable<Feature>, // Called by the transparency handle when changing the feature state
private val featureEditCallback: IFeatureEditCallback,
delegateHandles: IEditHandles,
context: FeatureEditContext
) :
EditHandlesWrapper(delegateHandles) {
private lateinit var polyline // The current polyline. This is derived from _feature
: Observable<Polyline?>
private val transparencyHandle // The handle used to edit the transparency
: PointEditHandle
init {
// Create a handle that controls the transparency
transparencyHandle = createTransparencyHandle(context)
}
override fun getList(): List<IEditHandle> {
return super.getList().toMutableList().apply {
add(transparencyHandle)
}
}
private fun createTransparencyHandle(context: FeatureEditContext): PointEditHandle {
// Create a handle that changes the transparency when the handle is dragged
return handle
}
companion object {
private fun deriveLocationFromTransparency(
polyline: Polyline?,
feature: Feature
): Point? {
return polyline?.let {
val transparency = feature.getValue<Double>(EditingModelFactory.transparencyPropertyPath)!!.apply { coerceIn(0.1, 1.0) }
val location0 = polyline.getPoint(0)
val location1 = polyline.getPoint(1)
val fraction = transparency * 0.25
val geodesy = GeodesyCalculations.create(it.reference, it.interpolationType)
val newLocation = geodesy.interpolate(location0, location1, -fraction)
return Point(it.reference, newLocation!!)
}
}
fun dot(v1: Coordinate, v2: Coordinate): Double {
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
}
fun parametricDistanceOfClosestPointOnLine(
pt: Coordinate,
p0: Coordinate,
p1: Coordinate
): Double {
val dt0 = Coordinate(pt.x - p0.x, pt.y - p0.y, pt.z - p0.z)
val d10 = Coordinate(p1.x - p0.x, p1.y - p0.y, p1.z - p0.z)
val denominator = dot(d10, d10)
return if (denominator != 0.0) {
dot(dt0, d10) / denominator
} else 0.0
}
fun deriveTransparencyFromLocation(
handleLocation: Point,
polyline: Polyline
): Double {
val location0 = polyline.getPoint(0)
val location1 = polyline.getPoint(1)
var closestPointParam = parametricDistanceOfClosestPointOnLine(handleLocation.location, location0, location1);
closestPointParam = Math.min(closestPointParam, 0.0);
val closestPoint = Coordinate(
location0.x + (location1.x - location0.x) * closestPointParam,
location0.y + (location1.y - location0.y) * closestPointParam,
location0.z + (location1.z - location0.z) * closestPointParam
)
val geodesy = GeodesyCalculations.create(polyline.reference, polyline.interpolationType)
val distance01 = geodesy.distance2D(location0, location1)
val distanceTransparency = geodesy.distance2D(location0, closestPoint)
if (distance01 == null || distanceTransparency == null) {
return 1.0
}
val transparency = distanceTransparency / (distance01 * 0.25)
return transparency.coerceIn(0.1, 1.0)
}
}
}
Step 2 - Create a handles provider
To make sure that our custom IEditHandlesIEditHandlesIEditHandles is used, we must provide it through an
IFeatureHandlesProviderIFeatureHandlesProviderIFeatureHandlesProvider implementation. In this tutorial,
we introduce the CustomFeatureHandlesProvider class for this purpose.
This class mainly delegates to an existing IFeatureHandlesProviderIFeatureHandlesProviderIFeatureHandlesProvider, and also creates a new CustomFeatureEditHandles
instance when we call it.
CustomFeatureHandlesProvider::CustomFeatureHandlesProvider() : _delegateHandlesProvider(std::make_shared<FeatureHandlesProvider>()) {
}
bool CustomFeatureHandlesProvider::canProvide(const std::shared_ptr<Observable<Feature>>& feature, const std::shared_ptr<FeatureEditContext>& context) const {
return _delegateHandlesProvider->canProvide(feature, context);
}
std::shared_ptr<IEditHandles> CustomFeatureHandlesProvider::provide(std::shared_ptr<Observable<Feature>> feature,
const std::shared_ptr<FeatureEditContext>& context,
std::shared_ptr<IFeatureEditCallback> featureEditCallback) const {
auto delegateHandles = _delegateHandlesProvider->provide(feature, context, featureEditCallback);
return std::make_shared<CustomFeatureEditHandles>(std::move(feature), std::move(featureEditCallback), delegateHandles, context);
}
public sealed class CustomFeatureHandlesProvider : IFeatureHandlesProvider
{
private readonly IFeatureHandlesProvider _delegateHandlesProvider;
public CustomFeatureHandlesProvider()
{
_delegateHandlesProvider = new FeatureHandlesProvider();
}
public bool CanProvide(Observable<Feature> feature, FeatureEditContext context)
{
return _delegateHandlesProvider.CanProvide(feature, context);
}
public IEditHandles Provide(Observable<Feature> feature, FeatureEditContext context, IFeatureEditCallback featureEditCallback)
{
var delegateHandles = _delegateHandlesProvider.Provide(feature, context, featureEditCallback);
return new CustomFeatureEditHandles(feature, featureEditCallback, delegateHandles, context);
}
}
object CustomFeatureHandlesProvider : IFeatureHandlesProvider {
private val delegateHandlesProvider = FeatureHandlesProvider()
override fun canProvide(feature: Observable<Feature>, context: FeatureEditContext): Boolean {
return delegateHandlesProvider.canProvide(feature, context)
}
override fun provide(
feature: Observable<Feature>,
context: FeatureEditContext,
featureEditCallback: IFeatureEditCallback
): IEditHandles? {
return delegateHandlesProvider.provide(feature, context, featureEditCallback)?.let {
CustomFeatureEditHandles(feature, featureEditCallback, it, context)
}
}
}
Step 3 - Use the custom feature handles provider
We need to make sure that the editing framework uses our custom handles provider, so we must configure it
in an IFeatureEditConfigurationIFeatureEditConfigurationIFeatureEditConfiguration, and set that configuration
on the FeatureLayerFeatureLayerFeatureLayer.
// Use a custom edit configuration that changes the editing behavior for this layer
auto customEditConfiguration = std::make_shared<CustomEditConfiguration>();
// Register the configuration. This makes the layer editable by default
return FeatureLayer::newBuilder()
.model(model) //
.title("Tutorial 2")
.editConfiguration(customEditConfiguration)
.painter(std::make_shared<TransparencyFeaturePainter>())
.build();
// Use a custom edit configuration that changes the editing behavior for this layer
var customEditConfiguration = new CustomEditConfiguration();
// Register the configuration. This makes the layer editable by default
return FeatureLayer.NewBuilder()
.Model(model)
.Title("Tutorial 2")
.EditConfiguration(customEditConfiguration)
.Painter(new TransparencyFeaturePainter())
.Build();
// Use a custom edit configuration that changes the editing behavior for this layer
val customEditConfiguration = CustomEditConfiguration
// Register the configuration. This makes the layer editable by default
return FeatureLayer.newBuilder()
.model(model)
.title("Tutorial 2")
.editConfiguration(customEditConfiguration)
.painter(TransparencyFeaturePainter)
.build()