0

The Problem:

I have two polygons feature classes, let's call them "SourceAreas" and "DangerAreas". For each SourceArea, I need to know if water could flow to any DangerArea and if so, what is the minimum distance that the water would flow before it reaches any DangerArea.

Using ArcGIS Desktop 10.5.1.

What I Have Tried:

I have used a DEM to create a flow direction raster and then a flow accumulation raster with weights based on my SourceAreas (all other cells with a zero weight), and then a "Streams" feature class using the 'Stream to Feature' tool (Spatial Analyst -> Hydrology).

So now I can clearly see all possible water flows from all of my SourceAreas on the map, and visually I can see if they intersect any of my DangerAreas downstream.

However, there are a LOT of these and I need to automate the process, and to measure flow distances. The attached screenshot illustrates a small sample of my overall area of interest. The red areas are the SourceAreas, the yellow is the DangerAreas and the blue lines are the streams.

enter image description here

I have tried creating a geometric network from the streams feature class and was going to try to do a trace downstream from all the points where the streams intersect my SourceAreas. But this has not proved useful for this purpose (at least not without a lot of additional processing).

I then moved on to Linnear Referencing. Created routes from the stream feature class, and tried to Locate Features Along Routes. But after leaving it running for hours, I gave up. Again, even if it worked, I'm not sure I could use the results to achieve what I want.

So my third attempt was to create a Python script which finds all starting nodes of the stream network, then using cursors, traces each route to its terminus by connecting the 'to_node' of one feature to the 'from_node' of the next and builds a single simple line along the way by unioning the geometries of all the connecting line features. The resulting long line is then inserted (again using cursors) into a 'Routes' feature class. The idea being that every possible route that water could take is now represented by a single part line feature with no branches. I'm confident that this could be used to get the results I need - if it ever finished running. Edit: This took about 6 days to run, and by then I'd solved it using something similar to the answer provided below.

What is the best way to determine, for each SourceArea, what is the minimum distance to any downstream DangerArea?

Update - new 'Flow Distance' tool: I've just discovered that there is a new ArcGIS Tool called 'Flow Distance' that looks like it should do exactly what I want. Unfortunately, it is only available in ArcGIS 10.6 and up, and we are on 10.5.1 - just one version shy of having this tool.

  • Can be done in no time. Convert Dem to nodata under 1st d.zone. Fill, fdir, catchment, mask. Compute flow length statistics per s.zone. Minimum. Append empty table. Go to next d.zone. – FelixIP Aug 2 '18 at 19:22
  • I don't fully understand the procedure that you've summarised. But I think that you're suggesting to use the 'Watershed' tool with the DangerAreas as pour points, so that I then have a raster that defines all the cells that could flow into the DangerAreas. And then run 'Flow Length' only on the Watershed areas. That way the flow length values would only include flows that run into the DangerAreas. I'll see if I can figure out how to make that work. – Son of a Beach Aug 3 '18 at 1:34
  • I'll post solution soon. – FelixIP Aug 3 '18 at 2:02
0

Workflow:

import arcpy
from arcpy.sa import *
from arcpy import env
env.overwriteOutput = True

outTarget=r"in_memory\target"
sigleP=r"in_memory\pgon"
outTable=r"in_memory\zs"
curT=arcpy.da.InsertCursor("Many_2_Many",("FROM_FID","TO_FID","DISTANCE"))

with arcpy.da.SearchCursor("danger_zone",("FID","Shape@")) as cursor:
  for to_fid,shp in cursor:
    arcpy.CopyFeatures_management(shp,sigleP)
    arcpy.PolygonToRaster_conversion(sigleP, "OID", outTarget)
    target=arcpy.Raster(outTarget)
    holes=Con(IsNull(target),"DEM")
    outFill = Fill(holes)
    fDir = FlowDirection(outFill)
    wShed = Watershed(fDir, target, "VALUE")
    erasedSources=Con(~IsNull(wShed),"s_raster")
    flen=FlowLength(fDir,  "DOWNSTREAM")
    try:
      N=0
      arcpy.gp.ZonalStatisticsAsTable_sa(erasedSources, "VALUE", flen,outTable,"DATA", "MINIMUM")
      with arcpy.da.SearchCursor(outTable,("Value","MIN")) as rows:
        for from_fid, dist in rows:
          curT.insertRow((from_fid,to_fid,dist))
          N+=1
    except:pass
    arcpy.AddMessage("%s processed, %s hit" %(to_fid,N))
    arcpy.Delete_management("in_memory")

OUTPUT took under 1 min to produce:

enter image description here

I explained logic in my comments.

  • Thanks. After studying your earlier comments I did come up with a solution. It was not quite the same as this, but near enough. Of course my original question was simplified from reality to avoid over complication, and my solution has to match the actual reality. – Son of a Beach Aug 3 '18 at 4:04

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.