ThinkGeo.com    |     Documentation    |     Premium Support

How To Trim An Extent

I have a small utility application that generates some shape files for my main application. This utility application combines a number of zip code shapes from the ThinkGeo zip code library to create a combined shape in a new shape file. I have this working pretty well, thanks to some assistance from this forum.


The problem I'm having now is that, when the shape file is saved, the combined polygon looks alright but the extent is the entire world map and the polygon is just a little blip. Is there a way to save the new shape file with the extent set just to the combined polygon?


Thanks,


Bob Mc.



Bob, 
  
 First, please make sure you set Map1.CurrentExtent to the combined shape’s BoundingBox with the following code. 
  
 combinedShape.Open(); 
 Map1.CurrentExtent = combinedShape.GetBoundingBox(); 
 combinedShape.Close(); 
  
 If you still have problem, that means the BoundingBox in your shape file is not correct. Can you then provide us some code showing how you create the shape file?  
  
 Ben. 


Actually Ben, zooming to the extent does work. I think I found the source of my problem. 
  
 I was zooming to the bounding box of the entire DynamicOverlay object and I didn’t realize that I had a layer there that covered the entire U.S. 
  
 Here’s a follow up question: if I have several layers in the DynamicOverlay object and I want to zoom to show all, except the base layer that covers the U.S., can I set that one to invisible, get the bounding box, and set the visibility back on again? 
  
 Thanks, 
  
 Bob Mc.

Bob, 
  
 You cannot do it; visible property only makes sense for drawing. You can write a method and get the BoundingBox very simply. 
  
 
        // Get the boudingBox of the input Layers
        private RectangleShape GetBoundingBox(IEnumerable<Layer> layers)
        {
            RectangleShape rectangleShape = new RectangleShape(0,0,0,0);
            foreach (Layer layer in layers)
            {
                rectangleShape.ExpandToInclude(layer.GetBoundingBox());
            }
            return rectangleShape;
        }
 
 Maybe we can add an “Enable” Property in the future which enable/disable all the methods relates to that layer.  We will see if there are any more requirements for this.  
  
 Ben 


I don’t think an additional “Enabled” property is necessary Ben. Your solution is straightforward enough and makes sense. 
  
 Thanks for the assitance. 
  
 Bob Mc.

Sorry Ben, it appears that I marked this as resolved too quickly. I'm still zooming out to the entire world map when I get the bounding boxes.


Here's my code to get the bounding box, similar to yours:


 



Private Function GetBoundingBox() As RectangleShape 
Dim result As New RectangleShape 
For Each l As ShapeFileFeatureLayer In ctlDirMap.DynamicOverlay.Layers 
If l.Name <> COUNTY_LAYER_NAME Then 
l.FeatureSource.Open() 
Dim feats As System.Collections.ObjectModel.Collection(Of Feature) = l.FeatureSource.GetAllFeatures(New String() {"FEATID"}) 
l.FeatureSource.Close() 
result.ExpandToInclude(feats(0).GetBoundingBox()) 
End If 
Next 
Return result 
End Function 


 


No matter if I get the bounding box of the layer or the feature it still results in the rectangle being height 206994.976 and width 175000.02814.


I think the problem may be in the code I'm using to create the shape files. I think the rectangle size of the shape file is the entire world map. Here's a shortened snipper of the code I'm using to create the shape files, adapted from something you posted for me in another post:



Dim lastUSState As String = String.Empty 
Dim zipLayer As ShapeFileFeatureLayer = Nothing 
Dim zipShapes As New System.Collections.ObjectModel.Collection(Of AreaBaseShape) 
Dim mapShapeLayer As New InMemoryFeatureLayer 
mapShapeLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle.FillSolidBrush.Color = GeoColor.FromArgb(150, GeoColor.SimpleColors.Gold) mapShapeLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20 
mapShapeLayer.InternalFeatures.Clear() 

For Each zipRow In zipRows 
If zipRow.STATE <> lastUSState Then 
lastUSState = zipRow.STATE 

If zipLayer IsNot Nothing Then 
zipLayer.Close() 
zipLayer = Nothing 
End If 

zipLayer = New ShapeFileFeatureLayer(zipMapSourcePath & zipRow.STATE & "\" & zipRow.STATE & "zcta5cu.shp") 
zipLayer.RequireIndex = False 
zipLayer.Open() 
End If 

zipFeature = zipLayer.FeatureSource.GetFeatureById(zipRow.GIST_ID.ToString, New String() {"GIST_ID", "COUNTY", "ZCTA"}) 
zipFeature.ColumnValues.Add("FEATID", featureId) 
zipShapes.Add(zipFeature.GetShape()) 
Next 

Dim combinedZips As MultipolygonShape = AreaBaseShape.Union(zipShapes) 
mapShapeLayer.Open() 

Dim proj4 As New Proj4Projection(Proj4Projection.GetEpsgParametersString(4326), Proj4Projection.GetGoogleMapParametersString()) mapShapeLayer.FeatureSource.Projection = proj4 
mapShapeLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle.OutlinePen.Color = GeoColor.FromArgb(255, GeoColor.SimpleColors.Black) 
Dim dict As New System.Collections.Generic.Dictionary(Of String, String) 
dict.Add("FEATID", featureId) 
mapShapeLayer.InternalFeatures.Add(featureId, New Feature(combinedZips, dict)) 
mapShapeLayer.Close() 
Dim dbfColumns As New Collection(Of DbfColumn) 
dbfColumns.Add(New DbfColumn("FEATID", DbfColumnType.String, 6, 0)) 
ShapeFileFeatureSource.CreateShapeFile(ShapeFileType.Polygon, outPath & "\" & featureId & ".shp", dbfColumns) 
ShapeFileFeatureLayer.BuildIndexFile(outPath & "\" & featureId & ".shp") 
newShapeFile = New ShapeFileFeatureLayer(outPath & "\" & featureId & ".shp", ShapeFileReadWriteMode.ReadWrite) 
newShapeFile.Open() 
newShapeFile.EditTools.BeginTransaction() 
Dim feature1 = New Feature(mapShapeLayer.InternalFeatures.Item(featureId).GetWellKnownText) 
feature1.ColumnValues.Add("FEATID", featureId) 
newShapeFile.EditTools.Add(feature1) 
Dim results As TransactionResult = newShapeFile.EditTools.CommitTransaction() 
newShapeFile.Close() 


Somehow this creates a shape file in which the feature shows up fine but the bounding box is the size of the entire world map.


Thanks, as always, for your expert assistance.


Bob Mc.


 



Bob,


Here are some suggestions for your code.

1, your method GetBoundingBox() doesn’t merge the boundingBox of every layer, instead it merges the boudingBox of the 1st record of every layer. I’m not sure if that’s what you want, (especially I saw you generate the shape file which has only one record), but seems a bit strange.

2, you have a judgment that you will only do the merging for the layers whose name not equal to COUNTY_LAYER_NAME. I just wonder did you set the name for layers before. If not, all the layers’ name will be empty and every layer will join the merging. 

3, Here is a good way to figure out the issue. You can print some info out in the GetBoundingBox() method, execute and analyze the result. That’s a powerful method and you can debug efficiently. The debug info will be shown in Output window which you can open in your VS by going to “View”->”Output”.



Private Function GetBoundingBox() As RectangleShape
        Dim result As New RectangleShape
        For Each l As ShapeFileFeatureLayer In ctlDirMap.DynamicOverlay.Layers
            If l.Name <> COUNTY_LAYER_NAME Then
                l.FeatureSource.Open()
                Dim boundingBox As RectangleShape = l.GetBoundingBox()
                result.ExpandToInclude(boundingBox)

                System.Diagnostics.Debug.WriteLine("Layer: " + l.ShapePathFileName)
                System.Diagnostics.Debug.WriteLine("BoundingBox Information: ")
                System.Diagnostics.Debug.WriteLine("Width: " + boundingBox.Width)
                System.Diagnostics.Debug.WriteLine("Height: " + boundingBox.Height)
                System.Diagnostics.Debug.WriteLine("")

                l.FeatureSource.Close()
            End If
        Next
        Return result
    End Function

I think that will solve your problem. Any queries please let us know.


Ben

 



Ben, 
  
 1. Yes, there is one feature per shape file, consequently one per layer, so I was getting the first feature’s bounding box. I was unaware that I could get the BoundingBox of the entire feature source. 
 2. I’m setting the name for every layer, and the County layer is one that I want to exclude from zoom expansion, hence the check for that layer. 
 3. Thanks for the tip on debug output. I’m very familiar with the technique, having programmed back in the days before IDEs and integrated debuggers when this was the <i>only</i> way to debug (yes, I’m a dinosaur). So I already had debug statements sprinkled liberally througout the process. Some example output: 
  
 Layer Name: D45498 
 Layer: C:\Work\Data\ThinkGeo\USA 2006\Directory\045498.shp 
 BoundingBox Information:  
 Width: 175000.028140549 
 Height: 206994.976463423 
  
 Layer Name: D3067 
 Layer: C:\Work\Data\ThinkGeo\USA 2006\Directory\003067.shp 
 BoundingBox Information:  
 Width: 12825436.1288003 
 Height: 4313305.73419233 
  
 Layer Name: D6672 
 Layer: C:\Work\Data\ThinkGeo\USA 2006\Directory\006672.shp 
 BoundingBox Information:  
 Width: 13148501.4324085 
 Height: 4347007.52512803 
  
 Layer Name: D45057 
 Layer: C:\Work\Data\ThinkGeo\USA 2006\Directory\045057.shp 
 BoundingBox Information:  
 Width: 12795013.9591199 
 Height: 4318629.93566564 
  
 So it seems that the shape files do have slightly different extents. I’m starting to wonder if this is a projection issue, something that keeps biting me in the butt. The map control’s MapUnit is set to GeographyUnit.Meter because I have a Google Map background. This is as per some code you posted in another thread. Do I need to set the projection somewhere when using: 
  
 
Dim proj4 As New Proj4Projection(Proj4Projection.GetEpsgParametersString(4326), Proj4Projection.GetGoogleMapParametersString())
 
  
 and setting the projection property of …something… somewhere in this method? 
  
 Bob Mc. 


Bob, 



I didn’t go through that time but sounds interesting. I can feel debugging like that brings a lot of efficiency but I can hardly imagine how to write a (not too small) project without IDE. I remember once debug some assembly code at school, with only a notepad and a build tool, oh my god! Thank goodness we can use IDE now. :) 



Anyway, thanks for the debug info first that helps a lot. 



From that I saw 3 of the 4 layers (D3067, D6672, D45057) might represents something covering the whole US and the other layer D45498 is much smaller, which might be the combined shape you created. The BoundingBox of those layers make sense as you can see from the online sample here (move the mouse and see the mouse coordinates) that in GoogleMap, US’ bounding box is about 13000000 meters wide and 4000000 meters height (without Alaska). 



So if you just want to see the combined shape, I think you should set the current extent to the BoundingBox of layer D45498, which should work. 



One thing to be aware is that the shape file you created seems been projected, so you do not need to project it again when you display them in GoogleMap. Also could you send me one of your combined shape file if you still have problem, which can help us recreate it here? 



Btw: Your code for getting a new projection is correct. I saw in your code there is a mapShapeLayer which doesn’t do much work just transforming one feature from one projection to another. In fact you can do it directly with proj4Projection as following. 




Dim dict As New System.Collections.Generic.Dictionary(Of String, String)
            dict.Add("FEATID", featureId)
            Dim feature As New Feature(combinedZips, dict)
            Dim proj4 As New Proj4Projection(Proj4Projection.GetEpsgParametersString(4326), Proj4Projection.GetGoogleMapParametersString())
            ‘ transform the feature from Geodetic to the one GoogleMap uses
    proj4.Open()
            Dim featureUnderGoogleMapProjection As Feature = proj4.ConvertToExternalProjection(feature)
    proj4.Close()





Hope that helps, any queries just let me know. 



Ben 

 



Ben, 
  
 The thing is, those layers should not cover the entire U.S. That’s why I think my shape file creation process, in the second code example from my post of 12-03-08, has some flaw that is creating a shape file with a bounding box that covers the whole U.S. while it really should be for a couple of adjacent zip codes. 
  
 As I noted in an earlier post, what I’m trying to achieve is a zoom out that will include all the displayed features, so zooming to the bounding box of only one of them doesn’t help. It seems that the bounding boxes are incorrect if they’re covering the whole U.S. 
  
 Thanks for the tip on converting projections. I’m still learning all the ins and outs of the API. 
  
 Bob Mc.

Bob,


Here I made a sample for you. It merges the shape features, get a proper bounding box and show them out. I tested with Tiger 2006 Data and it works fine. I simplify your code and hope that helps.


Any queries please let me know.


Ben


 


 



216-GenerateShapeForBob.zip (5.74 KB)