Select to view content in your preferred language

Best method to export as KML from user defined polygon?

7029
8
Jump to solution
11-02-2012 07:36 AM
Jay_Gregory
Frequent Contributor
I'm trying to create a web app that allows a user to draw a polygon, and then the ability to export the features within that polygon.  I partially got it working using a GP service based on a ModelBuilder model, but it seems to choke randomly (when a larger areas is chosen, but the number of features that should be returned is still far less than 1000).  The server generates no errors when I look at the logs, but the JS api claims "Job 'j024c31e288b2441a9f429e093f8fce6e' does not exist or is inaccessible."  The directory is clearly accessible and the kmz file is present. 

Anyway, was wondering if I might have better look directly making an esri.request and passing in the geometry.  The only thing I can't figure out is how an esri.request can handle a kmz file.  The only options are text, json, xml, but nothing that supports binary.  Is my only choice to troubleshoot the geoprocessing service?

Thanks, Jay
0 Kudos
1 Solution

Accepted Solutions
JeffJacobson
Frequent Contributor
Thanks!  I think that is my problem - I would like to use the REST API, however can't figure out how to make the GET request (passing in the filter geometry to the map service and setting the export to KMZ) work for KMZ prgramatically.  The reason I can't figure it out is because, as far as I know, GET requests can only be handled as text (json, text, html), but not as binary (zip, kmz).  So I was wondering is there was a way around this (or if I have it entirely wrong).  It here any ways browsers can handle binary responses from http get requests?  The api says to use esri.request but I can't get it to deliver a KMZ. 

Thanks for your assistance!

Jay


See this example.

View solution in original post

0 Kudos
8 Replies
JeffJacobson
Frequent Contributor
I wouldn't use a geoprocessing service for this task.  I implemented KML export for an app. using an ASP.NET generic handler (.ashx), using the Google Earth KML API for .NET.

private static geGeometry JsonToKmlGeometry(Dictionary<string, object> geometry)
{
    geGeometry placemarkGeometry;
    if (geometry.Keys.Contains("x"))
    {
        // Create point geometry
        // x and y are decimals;
        var x = Convert.ToDouble(geometry["x"]);
        var y = Convert.ToDouble(geometry["y"]);
        placemarkGeometry = new gePoint(new geCoordinates(new geAngle90(y), new geAngle180(x)));
    }
    else if (geometry.Keys.Contains("rings"))
    {
        // Create polygon geometry
        var rings = (ArrayList)geometry["rings"];

        var linearRings = from ArrayList ring in rings
                          select new geLinearRing((from ArrayList point in ring
                                                   select new geCoordinates(
                                                       new geAngle90(Convert.ToDouble(point[1])),
                                                       new geAngle180(Convert.ToDouble(point[0]))
                                                    )).ToList());
        if (linearRings.Count() > 1)
        {
            var polygon = new gePolygon(new geOuterBoundaryIs(linearRings.ElementAt(0)));
            polygon.InnerBoundaries.AddRange(from ring in linearRings.Skip(1)
                                             select new geInnerBoundaryIs(ring));
            placemarkGeometry = polygon;
        }
        else
        {
            placemarkGeometry = linearRings.First();
        }
    }
    else if (geometry.Keys.Contains("paths"))
    {
        // Create line geometry
        var paths = (ArrayList)geometry["paths"];

        var lineStrings = from ArrayList ring in paths
                          select new geLineString((from ArrayList point in ring
                                                   select new geCoordinates(
                                                       new geAngle90(Convert.ToDouble(point[1])),
                                                       new geAngle180(Convert.ToDouble(point[0]))
                                                    )).ToList());
        if (lineStrings.Count() > 1)
        {
            var multiGeo = new geMultiGeometry();
            multiGeo.Geometries.AddRange(lineStrings);
            placemarkGeometry = multiGeo;
        }
        else
        {
            placemarkGeometry = lineStrings.First();
        }

    }
    else
    {
        placemarkGeometry = null;
    }
    return placemarkGeometry;
}


if (Regex.IsMatch(format, "(?i)km[lz]"))
{
    // Generate a KML document.
    var kmlDocument = new geDocument();
    var jsSerializer = new JavaScriptSerializer();

    // Loop through the layers.
    var layers = jsSerializer.Deserialize<Dictionary<string, object>>(json);


    foreach (var kvp in layers)
    {
        var folder = new geFolder { Name = kvp.Key };
        kmlDocument.Features.Add(folder);
        var graphics = kvp.Value as ArrayList;
        foreach (Dictionary<string, object> graphic in graphics)
        {
            var placemark = new gePlacemark();
            folder.Features.Add(placemark);
            placemark.Geometry = JsonToKmlGeometry(graphic["geometry"] as Dictionary<string, object>);
            var attributesJson = (Dictionary<string, object>)graphic["attributes"];
            attributesJson.Remove("RouteGeometry");
            ////if (attributesJson.Keys.Contains("BufferedGeometry") && attributesJson.Keys.Contains("BufferSize") && Convert.ToDouble(attributesJson["BufferSize"]) > 0)
            ////{
            ////    // TODO: Set the placemark geometry to a multi-geometry containing both the main and buffered geometries.
            ////}
            string desc = ToHtmlDL(attributesJson);
            placemark.Description = desc;

        }
    }

    var kml = new geKML(kmlDocument);

    // Export the geKML into either KML or KMZ, depending on the specified format.
    byte[] bytes;
    if (string.Compare(format, "kmz", true) == 0)
    {
        bytes = kml.ToKMZ();
        context.Response.ContentType = "application/vnd.google-earth.kmz";
        context.Response.AddHeader("Content-Disposition", "filename=ExportedGraphics.kmz");
    }
    else
    {
        bytes = kml.ToKML();
        context.Response.ContentType = "application/vnd.google-earth.kml+xml";
        context.Response.AddHeader("Content-Disposition", "filename=ExportedGraphics.kml");
    }
    context.Response.BinaryWrite(bytes);
}
0 Kudos
Jay_Gregory
Frequent Contributor
Thanks!  This is a great example.  However, we haven't integrated .NET customization into our Arc environment yet.  Was wondering if there is a way to do this with Python (Model Builder) or just plain JavaScript with REST calls...

Thanks, Jay
0 Kudos
JeffJacobson
Frequent Contributor
I just reread your initial question and realized that I misunderstood what you were asking.

I think the best way to achieve this would be to use the ArcGIS Server REST API to perform a spatial query against the map service layer.  Various outputs can be supported this way, including KMZ (compressed KML).
0 Kudos
Jay_Gregory
Frequent Contributor
Thanks!  I think that is my problem - I would like to use the REST API, however can't figure out how to make the GET request (passing in the filter geometry to the map service and setting the export to KMZ) work for KMZ prgramatically.  The reason I can't figure it out is because, as far as I know, GET requests can only be handled as text (json, text, html), but not as binary (zip, kmz).  So I was wondering is there was a way around this (or if I have it entirely wrong).  It here any ways browsers can handle binary responses from http get requests?  The api says to use esri.request but I can't get it to deliver a KMZ. 

Thanks for your assistance!

Jay
0 Kudos
Jay_Gregory
Frequent Contributor
Using clip, MakeFeatureLayer, LayerToKML worked perfectly for this, in case anyone was wondering....
0 Kudos
JeffJacobson
Frequent Contributor
Thanks!  I think that is my problem - I would like to use the REST API, however can't figure out how to make the GET request (passing in the filter geometry to the map service and setting the export to KMZ) work for KMZ prgramatically.  The reason I can't figure it out is because, as far as I know, GET requests can only be handled as text (json, text, html), but not as binary (zip, kmz).  So I was wondering is there was a way around this (or if I have it entirely wrong).  It here any ways browsers can handle binary responses from http get requests?  The api says to use esri.request but I can't get it to deliver a KMZ. 

Thanks for your assistance!

Jay


See this example.
0 Kudos
Jay_Gregory
Frequent Contributor
Oh that is handy!  Thanks!
0 Kudos
zhengniu1
Deactivated User

Jay

How did you resolve this issue without using ArcGIS Geoprocessing Service? The example Jeff Jacobson provided is no longer available.

Many Thanks

0 Kudos