Hi,
I am working on my dissertation right now and need to use Arcgis to calculate the distance from the nearest fire to each house.
Since fire is polygon and house is point, I just use near tool to calculate distance from the nearest fire to the house. However the fires I am interested in is the fire that burned within the past 7 years before the sale of the homes. So I need to exclude all fires that burned after the sale date of the home, and all fires that burned greater than seven years before the sale of the home.
Is there anyway to add a time constraint to near tool using python?
thanks,
chelsea
Solved! Go to Solution.
OK, I had some fun with the data and this is what I obtained as result (houses drawn on distance to nearest fire):
Preparation:
Change in the code below:
Code:
import arcpy from datetime import datetime, date, timedelta from dateutil.relativedelta import relativedelta def main(): # input fc's fc_house = r"C:\Forum\DistFireHouse\shp\housesaledata.shp" fc_fire = r"C:\Forum\DistFireHouse\shp\firehistory_NAD_fips3002_feet.shp" # input field fld_date_sale = "ma_date_dt" # output fields fld_dist = "Dist2Fire" fld_fire_oid = "FireOID" # add fields addField(fc_house, fld_dist, "DOUBLE") addField(fc_house, fld_fire_oid, "LONG") # loop through houses cnt = 0 flds = ("SHAPE@", fld_date_sale, fld_dist, fld_fire_oid) with arcpy.da.UpdateCursor(fc_house, flds) as curs: for row in curs: cnt += 1 if cnt % 25 == 0: print "Processing house: {0}".format(cnt) pnt = row[0] date_sale = row[1] row[2], row[3] = getNearestFire(fc_fire, pnt, date_sale) curs.updateRow(row) def addField(fc, fldname, fldtype): if len(arcpy.ListFields(fc, wild_card=fldname)) == 0: arcpy.AddField_management(fc, fldname, fldtype) def getNearestFire(fc, pnt, date_sale): # input fields fld_year = "FIRE_YEAR" fld_month = "FIRE_MONTH" fld_day = "FIRE_DAY" # defaults min_dist = 999999 oid_fire = -1 flds = ("SHAPE@", "OID@", fld_year, fld_month, fld_day) with arcpy.da.SearchCursor(fc, flds) as curs: for row in curs: polygon = row[0] oid = row[1] year = int(row[2]) try: month = int(row[3]) except: month = 6 try: day = int(row[4]) except: day = 15 if chkDate(date_sale, year, month, day): dist = pnt.distanceTo(polygon) if dist < min_dist: min_dist = dist oid_fire = oid return min_dist, oid_fire def chkDate(date_sale, year_fire, month_fire, day_fire): date_fire = datetime(year=year_fire, month=month_fire, day=day_fire) date_2monthsbefore_sale = date_sale + relativedelta(months=-2) year_sale = date_sale.year test = (year_fire >= year_sale - 7) and (date_fire < date_2monthsbefore_sale) ## if test: ## print "date_sale: {0}".format(date_sale) ## print "date_fire: {0}".format(date_fire) ## print "date_2monthsbefore_sale: {0}".format(date_2monthsbefore_sale) ## print "year_sale: {0}".format(year_sale) ## print "{0} < {1} < {2}".format(year_sale-7, date_fire, date_2monthsbefore_sale) return test if __name__ == '__main__': main()
Can you just apply a definition query on the fire layer that meets your requirements then run your tool? Or do you have a specific python script that needs modified to accomplish this?
I assume that the sale of each house can have a different date, right? In that case the definition query on the fires per house would be different. In that case you don't really need to use the Near tool, but you could use the geometries to determine the distance. Do you have a maximum search distance defined?
This would require a loop through the houses, determine the date of sale, define the where clause based on the date to use in the search cursor for the fire polygons, calculate the distance, keep track of the shortest distance and store this as a result probably as an attribute for the current house.
If that is what you are looking for, and maybe if you can share a small part of your data, we can create a script to get that result.
Xander, you are right! Each house has a different sale date. I didn't
define the maximum search distance.
What you said is exactly what I want to do! I tried to do this manually but
it took so much time!
I attached part of my house sale data and fire history data. Parcelid is
parcel id for each house. Ma_date_dt is sale date of each house.
For each fire, I have fire_day, fire_month and fire_year!
If a house is sold on 07/15/2013, I would like to find the nearest fire to
this house that burned between 2006 and 05/2013.
if a house is sold on 05/02/2011, I would like to find the nearest fire to
the house that burned between 2004 to 03/2011.
I would like to restrict the beginning time to year but the end time
to month/year.
thanks so much,
Chelsea
the end time of fire need to be 2 month earlier than house sale time since
most sale process took longer than 2 month.
OK, I had some fun with the data and this is what I obtained as result (houses drawn on distance to nearest fire):
Preparation:
Change in the code below:
Code:
import arcpy from datetime import datetime, date, timedelta from dateutil.relativedelta import relativedelta def main(): # input fc's fc_house = r"C:\Forum\DistFireHouse\shp\housesaledata.shp" fc_fire = r"C:\Forum\DistFireHouse\shp\firehistory_NAD_fips3002_feet.shp" # input field fld_date_sale = "ma_date_dt" # output fields fld_dist = "Dist2Fire" fld_fire_oid = "FireOID" # add fields addField(fc_house, fld_dist, "DOUBLE") addField(fc_house, fld_fire_oid, "LONG") # loop through houses cnt = 0 flds = ("SHAPE@", fld_date_sale, fld_dist, fld_fire_oid) with arcpy.da.UpdateCursor(fc_house, flds) as curs: for row in curs: cnt += 1 if cnt % 25 == 0: print "Processing house: {0}".format(cnt) pnt = row[0] date_sale = row[1] row[2], row[3] = getNearestFire(fc_fire, pnt, date_sale) curs.updateRow(row) def addField(fc, fldname, fldtype): if len(arcpy.ListFields(fc, wild_card=fldname)) == 0: arcpy.AddField_management(fc, fldname, fldtype) def getNearestFire(fc, pnt, date_sale): # input fields fld_year = "FIRE_YEAR" fld_month = "FIRE_MONTH" fld_day = "FIRE_DAY" # defaults min_dist = 999999 oid_fire = -1 flds = ("SHAPE@", "OID@", fld_year, fld_month, fld_day) with arcpy.da.SearchCursor(fc, flds) as curs: for row in curs: polygon = row[0] oid = row[1] year = int(row[2]) try: month = int(row[3]) except: month = 6 try: day = int(row[4]) except: day = 15 if chkDate(date_sale, year, month, day): dist = pnt.distanceTo(polygon) if dist < min_dist: min_dist = dist oid_fire = oid return min_dist, oid_fire def chkDate(date_sale, year_fire, month_fire, day_fire): date_fire = datetime(year=year_fire, month=month_fire, day=day_fire) date_2monthsbefore_sale = date_sale + relativedelta(months=-2) year_sale = date_sale.year test = (year_fire >= year_sale - 7) and (date_fire < date_2monthsbefore_sale) ## if test: ## print "date_sale: {0}".format(date_sale) ## print "date_fire: {0}".format(date_fire) ## print "date_2monthsbefore_sale: {0}".format(date_2monthsbefore_sale) ## print "year_sale: {0}".format(year_sale) ## print "{0} < {1} < {2}".format(year_sale-7, date_fire, date_2monthsbefore_sale) return test if __name__ == '__main__': main()
thanks so much, Xander!
I am a Econ student and don't have any programming skills. I tried read
your script but can only understand a few.
I will run your code and compare it to results that I got earlier to see
whether they are consistent.
Except for fire history in the past 7 years, I also would like to do fire
history in the past 10, 15 and 20 years.
It seems to me that I only need to change the code in the following 2 lines:
def chkDate(date_sale, year_fire, month_fire, day_fire):
date_fire = datetime(year=year_fire, month=month_fire, day=day_fire)
date_2monthsbefore_sale = date_sale + relativedelta(months=-2)
year_sale = date_sale.year
test = (year_fire >= year_sale - 7) and (date_fire <
date_2monthsbefore_sale)
if test:
print "date_sale:
".format(date_sale)
".format(date_fire)
print "date_2monthsbefore_sale:
".format(date_2monthsbefore_sale)
".format(year_sale)
print " < < ".format(year_sale-7, date_fire,
date_2monthsbefore_sale)
return test
if __name__ == '__main__':
main()
Am I right? Are there any other changes that I need to make?
thanks,
Chelsea
You are right. In fact the only change would be on line 76, where the number 7 should be replaced by the number of years you want to use. It is not necessary to change it on line 82 since it is switched of (commented), but if you uncomment the lines, you would have to adapt that number too.
BUT, also make sure that you point the result to different output fields or to a different copy of your input input houses. If you don't the new run will overwrite the data in the existing fields.
If you consider that an answers has been helpful you can mark an answer as such using the link "Helpful Yes | No" below each post.
thanks for the reminder about overwriting problem.
Actually I get confused starting at # loop through houses! Is this loop
still consistent with what you posted earlier? For each house, find all
fires in the past 7 years based on house sale date. Then calculate distance
of each fire to the house and store the shorted one!
Chelsea
If you are interested in understanding the code, I can give you some pointers, but this example may not be the best way of learning python. But, anyway, here goes:
That is basically it...
Kind regards, Xander