ThinkGeo.com    |     Documentation    |     Premium Support

Moving features in a layer

In our product, we save the raster layer and InMemoryLayers to file by serializing them. Due to a mistake in georeferencing a raster image, we have a bunch of layers that don't line up with their uderlaying raster layer anymore and we need to 'slide' every feature over a little bit by adding a user-specified value X and a user-specified value Y to every verticie of every shape in every InMemoryLayer.


How is the best way to do that?  I don't see any access to Feature verticies in the InternalFeatures collection of a Layer.  Surely the list of verticies is available somewhere.


Dave



David,


  I created a little sample for you that shows how to change a specific vertex of a polygon in an InMemoryFeatureLayer and update that change. This is a pretty simple sample but I think that from this you can extend it to your specific need. Here, I get the second vertex of my polygon change its X and Y and update it.

 




public void Post10007()
{
    wpfMap1.MapUnit = GeographyUnit.DecimalDegree;

    WorldMapKitWmsWpfOverlay worldMapKitWmsWpfOverlay = new WorldMapKitWmsWpfOverlay();
    worldMapKitWmsWpfOverlay.Projection = WorldMapKitProjection.DecimalDegrees;
    wpfMap1.Overlays.Add(worldMapKitWmsWpfOverlay);

    InMemoryFeatureLayer inMemoryFeatureLayer = new InMemoryFeatureLayer();
    inMemoryFeatureLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.CreateSimpleAreaStyle(new GeoColor(150, GeoColor.StandardColors.Red));
    inMemoryFeatureLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

    PolygonShape polygonShape = new PolygonShape();
    RingShape ringShape = new RingShape();
    ringShape.Vertices.Add(new Vertex(-96.8293,33.1042));
    ringShape.Vertices.Add(new Vertex(-96.8270,33.1043));
    ringShape.Vertices.Add(new Vertex(-96.8284,33.1005));
    ringShape.Vertices.Add(new Vertex(-96.8293,33.1042));
    polygonShape.OuterRing = ringShape;

    inMemoryFeatureLayer.Open();
    inMemoryFeatureLayer.FeatureSource.BeginTransaction();
    Feature feature = new Feature(polygonShape.GetWellKnownText(), "MyPolygon");
            
    inMemoryFeatureLayer.FeatureSource.AddFeature(feature);
    inMemoryFeatureLayer.FeatureSource.CommitTransaction();
    inMemoryFeatureLayer.Close();

    LayerOverlay dynamicOverlay = new LayerOverlay();
    dynamicOverlay.Layers.Add(inMemoryFeatureLayer);
    wpfMap1.Overlays.Add(dynamicOverlay);

    wpfMap1.CurrentExtent = new RectangleShape(-96.840,33.110,-96.810,33.090);
}

private void button2_Click(object sender, RoutedEventArgs e)
{
    LayerOverlay dynamicOverlay =  (LayerOverlay)wpfMap1.Overlays[1];
    InMemoryFeatureLayer inMemoryFeatureLayer = (InMemoryFeatureLayer)dynamicOverlay.Layers[0];
    PolygonShape polygonShape = (PolygonShape)inMemoryFeatureLayer.FeatureSource.GetFeatureById("MyPolygon", ReturningColumnsType.NoColumns).GetShape();

    polygonShape.OuterRing.Vertices[1] = new Vertex(-96.8231, 33.1054);
    polygonShape.Id = "MyPolygon";

    inMemoryFeatureLayer.Open();
    inMemoryFeatureLayer.EditTools.BeginTransaction();
    inMemoryFeatureLayer.EditTools.Update(polygonShape);
    inMemoryFeatureLayer.EditTools.CommitTransaction();
    inMemoryFeatureLayer.Close();

    wpfMap1.Refresh(dynamicOverlay);
}


 Thank you for that code...  it was a great help.  Using it I wrote the follow to move every vertex in every shape a specified distance. It doesn't seem to work however...  can you see what I did wrong?


Dave






foreach (LayerOverlay lo in myMap.Overlays)
{
    foreach (InMemoryFeatureLayer fl in lo.Layers)
    {
        List<Feature> features = fl.FeatureSource.GetAllFeatures(ReturningColumnsType.NoColumns).ToList();
        foreach (Feature f in features)
        {
            PolygonShape shape = (PolygonShape)fl.FeatureSource.GetFeatureById(f.Id, ReturningColumnsType.NoColumns).GetShape();
            for (int i = 0; i < features.Count; i++)
            {
                shape.OuterRing.Vertices[i] = new Vertex(shape.OuterRing.Vertices[i].X + X, shape.OuterRing.Vertices[i].Y + Y);
            }

            fl.Open();
            fl.EditTools.BeginTransaction();
            fl.EditTools.Update(shape);
            fl.EditTools.CommitTransaction();
            fl.Close();
        }
    }

    myMap.Refresh(lo);
}


David,


 If you are uniformly changing the X and Y of the vertices of your PolygonShape by a specified distance in X and Y, it makes more sense the treat the PolygonShape as a whole using the Translate function. See in the code below how this is done:



LayerOverlay dynamicOverlay =  (LayerOverlay)wpfMap1.Overlays[1];
InMemoryFeatureLayer inMemoryFeatureLayer = (InMemoryFeatureLayer)dynamicOverlay.Layers[0];
PolygonShape polygonShape = (PolygonShape)inMemoryFeatureLayer.FeatureSource.GetFeatureById("MyPolygon", ReturningColumnsType.NoColumns).GetShape();

//For changing only one vertex of the PolygonShape
//polygonShape.OuterRing.Vertices[1] = new Vertex(-96.8231, 33.1054);

//For changing all the vertices of the PolygonShape by the same specified distance in X and Y. 
//Or Translating the PolygonShape as a whole.
polygonShape.TranslateByOffset(0.0008, 0.0021); //Here I offset by 0.0008 in X and 0.0021 in Y.
polygonShape.Id = "MyPolygon";

inMemoryFeatureLayer.Open();
inMemoryFeatureLayer.EditTools.BeginTransaction();
inMemoryFeatureLayer.EditTools.Update(polygonShape);
inMemoryFeatureLayer.EditTools.CommitTransaction();
inMemoryFeatureLayer.Close();

wpfMap1.Refresh(dynamicOverlay);


 Makes sense...  however, when I run this, the verticies are not permenantly changed.  That is, if I watch the value of each vertex as this is run, I see the values change as expected after TranslateByOffset, but if I lok at the X,Y values after CommitTransaction() (by placing a breakpoint on fl.Close()), I see that the vertex values are back to their original values.


Why are you setting the value of polygonShape.Id? When I do GetShape(), the polygonShape alreay has a value for Id... looks like a Guid..


Dave


 



foreach (LayerOverlay lo in myMap.Overlays)
{
    foreach (Layer lay in lo.Layers)
    {
        if (lay.GetType() != typeof(GdiPlusRasterLayer))
        {
            InMemoryFeatureLayer fl = lay as InMemoryFeatureLayer;
            List<Feature> features = fl.FeatureSource.GetAllFeatures(ReturningColumnsType.NoColumns).ToList();
            fl.Open();
                                            
            foreach (Feature f in features)
            {
                PolygonShape shape = (PolygonShape)fl.FeatureSource.GetFeatureById(f.Id, ReturningColumnsType.NoColumns).GetShape();
                shape.TranslateByOffset(df.Drift.X, df.Drift.Y);
                fl.EditTools.BeginTransaction();
                fl.EditTools.Update(shape);
                fl.EditTools.CommitTransaction();
            }
                                            
            fl.Close();
        }                                        
    }
    myMap.Refresh(lo);
}


David,


 In my last sample code, where I set the Id of the shape is redundant. The Id is already there because I retrieve the shape using the GetFeatureById. On the other hand, it would have been necessary to set the Id of a shape if I had created it from scratch so that when calling the EditTools.Update function the shape gets assigned to the appropriate feature.


  I created a sample with your code and I am basically doing the same thing as you are doing. I am translating the shapes and I do not observe what you are describing with the vertex values reverting back to the their original values. I would need to see your code in the larger context you are using it. Can you send us a little sample app that we can run and see the problem happening? If you do not feel confortable with attaching it to this post, you can contact Support at support@thinkgeo.com and we will set you up with an FTP account if you don't have one already. Thank you.



 I used your code in a sample app of my own and do see that the general idea seems to work.  The issue does seem to be in the way we are implemeting layers.  Let me ask the question a different way:


In the following code, the value of all of the OuterRing verticies of 'shape' are being properly modified by the TranslateByOffset method. The  values are not being written back to the feature however.  If I look at the values of the OuterRing verticies of 'shape' and compare them to the values of the OuterRing vertices of the feature within newLayer after the completion of CommitTransaction(), I see that the vertex values of the feature remain as they were when I started.   The values of 'shape' are not being committed to the feature in newLayer.


Under what conditions could this happen? Could I somehow be unaware that I have 'locked' the feature or layer in such a way that the Update/CommitTransaction does not work? (and yet produce no errors)


Dave


 



List<Feature> features = newLayer.FeatureSource.GetAllFeatures(ReturningColumnsType.NoColumns).ToList();
newLayer.Open();
foreach (Feature f in features)
{
    PolygonShape shape = (PolygonShape)newLayer.FeatureSource.GetFeatureById(f.Id, ReturningColumnsType.NoColumns).GetShape();
    shape.TranslateByOffset(Drift.X, Drift.Y);
    newLayer.EditTools.BeginTransaction();
    newLayer.EditTools.Update(shape);
    newLayer.EditTools.CommitTransaction();
}
newLayer.Close();


Steve,


 I have not been able to recreate your problem. You see in my sample app, I created two features with polygon in an InMemoryFeatureLayer and I use your code to loop thru and translate. It works just fine visually on the map and I also double checked the values by doing a second loop after doing the CommitTransaction. See the code below with my example. As I said before, I think that you are going to have to send us a little sample where we can see the problem.


 



foreach (LayerOverlay lo in wpfMap1.Overlays)
{
foreach (Layer lay in lo.Layers)
{
    if (lay.GetType() != typeof(GdiPlusRasterLayer))
    {
        InMemoryFeatureLayer fl = lay as InMemoryFeatureLayer;
        List<Feature> features = fl.FeatureSource.GetAllFeatures(ReturningColumnsType.NoColumns).ToList();
        fl.Open();

        foreach (Feature f in features)
        {
            PolygonShape shape = (PolygonShape)fl.FeatureSource.GetFeatureById(f.Id, ReturningColumnsType.NoColumns).GetShape();
            //shape.TranslateByOffset(df.Drift.X, df.Drift.Y);

            double xValueBefore = shape.OuterRing.Vertices[0].X; //-96.8293 is the value before Translate

            shape.TranslateByOffset(0.0008, 0.0021);

            double yValueAfter = shape.OuterRing.Vertices[0].X; //-96.8285 is the value after Translate

            fl.EditTools.BeginTransaction();
            fl.EditTools.Update(shape);
            fl.EditTools.CommitTransaction();
        }

        fl.Close();
    }
}


//Here I loop again the check if the CommitTransaction worked.
foreach (Layer lay in lo.Layers)
{
    if (lay.GetType() != typeof(GdiPlusRasterLayer))
    {
        InMemoryFeatureLayer fl = lay as InMemoryFeatureLayer;
        fl.Open();
        List<Feature> features = fl.FeatureSource.GetAllFeatures(ReturningColumnsType.NoColumns).ToList();
                       
        foreach (Feature f in features)
        {
            PolygonShape shape = (PolygonShape)fl.FeatureSource.GetFeatureById(f.Id, ReturningColumnsType.NoColumns).GetShape();

            double yValueAfter = shape.OuterRing.Vertices[0].X; //-96.8285 is the value after Translate and CommitTransaction
                                                                //It has NOT reverted back to the value before Translate.
        }

        fl.Close();
    }
}