0

I am working on ArcObjects. I want to create shapefile. I am very close to it but need some better alternative as I feel my solution may fall short in time when there are thousand of records.

I have a geometry field in a database. From that I get the string which is Linstring, Multilinestring or Point

Eg.

"LINESTRING (-98.71702604799998 37.746413141000062, -98.717210399999942 37.738209632000064, -98.717347586999949 37.732091870000033)"

From this I manually extract the coordinates like

string d = st.Substring(12); // to get rid of LINESTRING label
d = d.Substring(0, d.Length - 1); // to get points as x1 y1,x2 y2,x3 y3
arr = d.Split(','); // now to get array [x1 y1, x2 y2, x3 y3]

Here is my code:

private IPolyline CreatePolyline(DataSet ds)
{
    ESRI.ArcGIS.Geometry.IPolyline pSegPoly1 = new ESRI.ArcGIS.Geometry.PolylineClass();

    pSegPoly1.SpatialReference = createSpatialReference();
    ESRI.ArcGIS.Geometry.IGeometryCollection pGeoColl12 = pSegPoly1 as ESRI.ArcGIS.Geometry.IGeometryCollection;

    foreach (DataRow row in ds.Tables[0].Rows)
    {
        string st = row["ShapeGeoWKT"].ToString();
        string[] arr = null;
        if (st.StartsWith("MULTI"))
        {
            string d = st.Substring(18);
            d = d.Substring(0, d.Length - 2);
            arr = d.Split(',');
        }
        else if (st.StartsWith("LINE"))
        {
            string d = st.Substring(12);
            d = d.Substring(0, d.Length - 1);
            arr = d.Split(',');
        }
        ESRI.ArcGIS.Geometry.IPoint[] pntArrayTemp = new ESRI.ArcGIS.Geometry.IPoint[arr.Length];
        int k = 0;
        ESRI.ArcGIS.Geometry.ILine[] linearray = new ESRI.ArcGIS.Geometry.LineClass[pntArrayTemp.Length];
        ESRI.ArcGIS.Geometry.ISegmentCollection[] paths = new ESRI.ArcGIS.Geometry.PathClass[pntArrayTemp.Length];
        foreach (string st1 in arr)
        {
            linearray[k] = new ESRI.ArcGIS.Geometry.LineClass();
            paths[k] = new ESRI.ArcGIS.Geometry.PathClass();
            pntArrayTemp[k] = new ESRI.ArcGIS.Geometry.PointClass();
            string[] temp1 = st1.Trim().Split(' ');
            pntArrayTemp[k].X = Convert.ToDouble(temp1[0].Replace(")", "").Replace("(", "").Trim());
            pntArrayTemp[k].Y = Convert.ToDouble(temp1[1].Replace(")", "").Replace("(", "").Trim());
            object obj1 = Type.Missing;
            paths[k].AddSegment((ISegment)linearray[k], ref obj1, ref obj1);
            k++;

        }
        for (int j = 0; j < paths.Length - 1; j++)
        {
            object obj12 = Type.Missing;
            pGeoColl12.AddGeometry((ESRI.ArcGIS.Geometry.IGeometry)paths[j], ref obj12, ref obj12);
            linearray[j].PutCoords(pntArrayTemp[j], pntArrayTemp[j + 1]);
        }

    }
    pGeoColl12.GeometriesChanged();
    return pGeoColl12 as IPolyline;
}

From this I get an array of the co-ordinates which I use to create shape for line. It is a very tedious procedure for many records (100k or more).

Is their any simple easy method in ArcObjects that I can use to get the coordinates from LINESTRING on the fly or I can use the LINESTRING to draw lines?

5
  • Don't cross-post: stackoverflow.com/questions/24365649/… -- please delete this question here; it's off topic and already asked at Stack Overflow
    – slhck
    Jun 23 '14 at 12:20
  • 3
    I'd almost argue that it belongs here, and not SO, except at least there the source was legible and posted as text. It seems you have two questions, one on how to parse Well-Known Text to obtain a list of point arrays (hint: MULTILINESTRING can be multi-part) and the other on how to efficiently assemble a multi-part polyline in ArcObjects (which is well documented)
    – Vince
    Jun 23 '14 at 12:43
  • 2
    I'd also note that the Subject is not addressed by the question, since there's a whole lot more to shapefile construction than making IGeometry objects.
    – Vince
    Jun 23 '14 at 12:49
  • 1
    Agreed, this post belongs here as it has a GIS component. I can't read that text so can't help at this stage but I have done this before and have working code which I could share but cannot ascertain what the OP is up to. Please remove image and insert code and I'll check back later Jun 23 '14 at 22:09
  • Hey Michael and Vince thanks for your reply. I have written my function here and removed the image please give me appropriate solution for it. Jun 24 '14 at 5:32
2

Shapefiles need a few things before they can be created, they are created in a workspace which in this case is a folder, they need fields and geometry definition. When all of those prerequisites are met it's fairly easy to create one. This is similar to creating a file or personal geodatabase feature class (they start from a different workspace factory)

// create a spatial reference object from EPSG Code
ISpatialReferenceFactory3 pSRgen = new SpatialReferenceEnvironmentClass();
pSR = pSRgen.CreateSpatialReference(EPSG_CODE);    

IFields pOutFields = new FieldsClass();
IFieldsEdit pFieldsEd = (IFieldsEdit)pOutFields;
IField pNewField;
IFieldEdit pNewFieldEd;
IGeometryDef pGeomDef = new GeometryDefClass();
IGeometryDefEdit pGDefEd = (IGeometryDefEdit)pGeomDef;    

// create the fields object from scratch
// start with objectid / fid
pNewField = new FieldClass();
pNewFieldEd = (IFieldEdit)pNewField;
pNewFieldEd.Name_2 = "FID";
pNewFieldEd.Type_2 = esriFieldType.esriFieldTypeOID;
pFieldsEd.AddField(pNewField);

// next shape
pNewField = new FieldClass();
pNewFieldEd = (IFieldEdit)pNewField;
pNewFieldEd.Name_2 = "Shape";
pNewFieldEd.Type_2 = esriFieldType.esriFieldTypeGeometry;
// geometry fields need a geometry def: geometry type, grid, spatial reference ...
pGDefEd.GeometryType_2 = esriGeometryType.esriGeometryPolyline;
pGDefEd.GridCount_2 = 1;
// grid and spatial reference valid for geographic coordinates
// change the values for projected coordinate systems
pGDefEd.set_GridSize(0, 0.1);
pSR.SetFalseOriginAndUnits(-180, -180, 1000000000);
// Associate the spatial reference with the GeometryDef
pGDefEd.SpatialReference_2 = pSR;
pNewFieldEd.GeometryDef_2 = pGeomDef;
pFieldsEd.AddField(pNewField);

// shapefiles must have at least one other field
// you can use this as a template to add new fields
pNewField = new FieldClass();
pNewFieldEd = (IFieldEdit)pNewField;
pNewFieldEd.Name_2 = "NextField";
pNewFieldEd.Type_2 = esriFieldType.esriFieldTypeInteger;
pFieldsEd.AddField(pNewField);

// now there's enough information to create the shapefile... sort of
// insert your own values here
string PutShapeHere = "d:\temp";
string ShapeName    = "My_Shapefile.shp";

// open the workspace and make it a feature workspace
IWorkspaceFactory pWSF = new ESRI.ArcGIS.DataSourcesFile.ShapefileWorkspaceFactoryClass();
IWorkspace pWS = pWSF.OpenFromFile(PutShapeHere,0);
IFeatureWorkspace pFeatWS = (IFeatureWorkspace)pWS;

// now to create the shapefile
pFeatWS.CreateFeatureClass(ShapeName,pOutFields,null,null, esriFeatureType.esriFTSimple, "Shape", "");
5
  • Michael Thank you for your reply but I have already done with this and I have successfully created file but it is very tedious procedure so I want to make it faster so. I have already done with this. Jun 24 '14 at 11:54
  • Yes it is a very tedious process, though that bit is not mentioned in your code sample. There is no faster way; you can write your own class to save having to rewrite it all again. Jun 24 '14 at 21:55
  • ok. thank you for your reply. and one more question to you have any idea about that how to add field value in the .dbf file when create shape file. i have complete with column name which i insert in it. but i did not get how to feel value in it. can you please explain for that. Jun 25 '14 at 6:19
  • @Michael This answer addresses the question title (quite well!), but not the question body (which, alas, is very different).
    – Vince
    Jun 28 '14 at 21:11
  • Yes @Vince, it appears that the question wasn't what it seemed. The OP wanted to make the code posted more efficient rather than to have somewhere to plug it in - which was what I understood from the question. Jun 29 '14 at 3:10
2

Note: This answers the question, as asked in the body (about efficient line generation from points), not the title (about how to create a shapefile).

As with most aspects of ArcGIS, there is more than one way to construct geometries. The most efficient methodology for bulk conversion (not user interaction), involves use of the ESRI.ArcGIS.esriSystem.WKSPoint type (vice ESRI.ArcGIS.Geometry.IPoint) and the ESRI.ArcGIS.Geometry.IGeometryBridge2 SetWKSPoints method, as described in the documentation.

Unfortunately, there is no form of IGeometryBridge2.SetWKSPoints that supports multi-part line construction with an array of offsets, so you would need to retain the by-part looping, caching the WKSPoint objects in a list until it was time to generate an array, and concatenating the Polyline objects (via IGeometryCollection.AddGeometry).

In Java, with method parameter ISpatialReference spatialRef and object properties _WKSPoint[] points and int[] parts (storing the start and stop offsets, of length nParts+1), a similar multi-part line generation code fragment was rendered:

    IGeometryBridge2 geometryBridge = new GeometryEnvironment();
    IGeometry geom = null;
    switch (this.geoType) {
    ...
    case POLYLINE_SHAPE:
        if (this.parts.length > 2) {        
            geom = new Polyline();
            geom.setSpatialReferenceByRef(spatialRef);
            IGeometryCollection lineColl = (IGeometryCollection)geom;
            int i = 0;
            for (int j = 1; j < this.parts.length; j++) {
                int count = this.parts[j] - this.parts[j-1];
                _WKSPoint[] tPoints = new _WKSPoint[count];
                for (int k = 0; k < count; k++) {
                    tPoints[k] = this.points[i++];
                }
                IGeometry tLine = new Polyline();
                tLine.setSpatialReferenceByRef(spatialRef);
                geometryBridge.setWKSPoints((IPointCollection4)tLine,tPoints);
                lineColl.addGeometry(tLine,null,null);
            }
        } else {
            geom = new Polyline();
            geom.setSpatialReferenceByRef(spatialRef);
            geometryBridge.setWKSPoints((IPointCollection4)geom,this.points);
        }
        break;
    ...
    }

I expect you can duplicate the effect in C# using an ArrayList of WKSPoint to cache the parsed coordinates (if you're a bit more sophisticated in the coordinate parsing and look for the "),(" which divides parts in a MULTIPOLYLINE).

I'm afraid that using ArcObjects is often going to be quite tedious, especially doing data conversion. If you don't have the patience to work with low-level building blocks, I suggest you try using the Python interface.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.