@Override
 public Layer createLayer(LayerContext ctx, PropertySet configuration) {
   Assert.notNull(ctx, "ctx");
   final ProductLayerContext plc = (ProductLayerContext) ctx;
   final String vectorDataName = (String) configuration.getValue(PROPERTY_NAME_VECTOR_DATA);
   final VectorDataNode vectorDataNode = plc.getProduct().getVectorDataGroup().get(vectorDataName);
   return createLayer(vectorDataNode, configuration);
 }
  @Override
  public Layer createLayer(LayerContext ctx, PropertySet configuration) {
    CoordinateReferenceSystem targetCrs = null;
    if (ctx != null) {
      targetCrs = (CoordinateReferenceSystem) ctx.getCoordinateReferenceSystem();
    }
    FeatureCollection<SimpleFeatureType, SimpleFeature> fc;
    fc =
        (FeatureCollection<SimpleFeatureType, SimpleFeature>)
            configuration.getValue(FeatureLayerType.PROPERTY_NAME_FEATURE_COLLECTION);
    if (fc == null) {
      try {
        final URL url =
            (URL) configuration.getValue(FeatureLayerType.PROPERTY_NAME_FEATURE_COLLECTION_URL);
        FeatureSource<SimpleFeatureType, SimpleFeature> featureSource =
            FeatureUtils.getFeatureSource(url);
        fc = featureSource.getFeatures();
      } catch (IOException e) {
        throw new IllegalArgumentException(e);
      }
    }
    final CoordinateReferenceSystem featureCrs =
        (CoordinateReferenceSystem)
            configuration.getValue(FeatureLayerType.PROPERTY_NAME_FEATURE_COLLECTION_CRS);
    final Geometry clipGeometry =
        (Geometry)
            configuration.getValue(FeatureLayerType.PROPERTY_NAME_FEATURE_COLLECTION_CLIP_GEOMETRY);

    fc =
        FeatureUtils.clipCollection(
            fc,
            featureCrs,
            clipGeometry,
            DefaultGeographicCRS.WGS84,
            null,
            targetCrs,
            ProgressMonitor.NULL);

    return new FeatureLayer(this, fc, configuration);
  }
  private synchronized void updateChildren() {

    // Collect all current mask layers
    LayerFilter layerFilter =
        layer -> {
          PropertySet conf = layer.getConfiguration();
          return conf.isPropertyDefined(MaskLayerType.PROPERTY_NAME_MASK)
              && conf.getValue(MaskLayerType.PROPERTY_NAME_MASK) != null;
        };
    List<Layer> maskLayers =
        LayerUtils.getChildLayers(
            LayerUtils.getRootLayer(this), LayerUtils.SEARCH_DEEP, layerFilter);
    HashMap<Mask, Layer> currentLayers = new HashMap<>();
    for (Layer maskLayer : maskLayers) {
      Mask mask = maskLayer.getConfiguration().getValue(MaskLayerType.PROPERTY_NAME_MASK);
      currentLayers.put(mask, maskLayer);
    }

    // Allign mask layers with available masks
    if (raster != null && getProduct() != null) {
      Mask[] availableMasks = getProduct().getMaskGroup().toArray(new Mask[0]);
      HashSet<Layer> unusedLayers = new HashSet<>(maskLayers);
      for (Mask availableMask : availableMasks) {
        // todo add all mask layers as soon as the masks have been scaled to fit the raster
        if (raster.getRasterSize().equals(availableMask.getRasterSize())) {
          Layer layer = currentLayers.get(availableMask);
          if (layer != null) {
            unusedLayers.remove(layer);
          } else {
            layer = createLayer(availableMask);
            getChildren().add(layer);
          }
          layer.setVisible(raster.getOverlayMaskGroup().contains(availableMask));
        }
      }

      // Remove unused layers
      for (Layer layer : unusedLayers) {
        layer.dispose();
        Layer layerParent = layer.getParent();
        if (layerParent != null) {
          layerParent.getChildren().remove(layer);
        }
      }
    }
  }