I have an interesting problem. I have been asked to create a set of polygons using dates. Below is an example table showing the data. As you can see the table is sorted by start year.
What I have to do is generate a set of polygons each time the start year, month and day changes. So the first polygon will just contain the first row of data. The second polygon will contain both the first and second and so on.
There is also another element to this. Each row of data has an end year, month and day. If a row has an end year of 2300 then that row will stay in the output once it has been added. If there is an end year that does not equal 2300 then that row will not be included in future versions of the output.
So is there anyone who can help? I have about two hundred tables like the one above. Would like a model builder solution.
Thanks in anticipation.
Paul
Solved! Go to Solution.
Hi Paul,
I think you could try something like this:
import arcpy, os
from datetime import datetime, timedelta
arcpy.env.overwriteOutput = True
# change path to datasource
fc = r"D:\Xander\GeoNet\MergePolsDate\Reserves_copy.shp"
fld_start = "DateFrom" # create date field and fill it with content of StartDate
fld_end = "DateTo" # create date field and fill it with content of EndDate
fld_name = "Name"
fld_outname = "OutputName" # output name
len_outname = 75
# output shape
fc_out = r"D:\Xander\GeoNet\MergePolsDate\Reserves_merged3.shp"
# create empty output shapefile
ws_path, fc_out_name = os.path.split(fc_out)
arcpy.CreateFeatureclass_management(ws_path, fc_out_name,
"POLYGON", fc, "SAME_AS_TEMPLATE", "SAME_AS_TEMPLATE", fc)
# add string field
arcpy.AddField_management(fc_out, fld_outname, "TEXT", "", "", len_outname)
flds = ("SHAPE@", fld_start, fld_end, fld_name)
flds_out = ("SHAPE@", fld_start, fld_name, fld_outname)
# create a unique list of dates from start date
lst_dates = list(set([row[0] for row in arcpy.da.SearchCursor(fc, (fld_start))]))
lst_dates.sort()
# insert cursor to store features to new featureclass
with arcpy.da.InsertCursor(fc_out, (flds_out)) as curs_out:
# loop through list of unique dates
for date in lst_dates:
# create an expression
expression = "{0} <= date '{2}' AND {1} >= date '{2}'".format(
arcpy.AddFieldDelimiters(fc, fld_start),
arcpy.AddFieldDelimiters(fc, fld_end), date)
i = 0
# create the searchcursor with the expression
with arcpy.da.SearchCursor(fc, flds, where_clause=expression) as curs:
for row in curs:
pol = row[0]
name = row[3] # take name of first polygon
if i == 0:
# first polygon is just the polygon"
polygon = pol
else:
# next polygons are added with union
polygon = polygon.union(pol)
i += 1
# insert the feature into the output featureclass
datefrom = date
i_date = lst_dates.index(date)
if i_date + 1 > len(lst_dates) - 1:
dateto = ""
outname = "{0} {1} - {2}".format(name, datefrom.strftime('%Y/%m/%d'), dateto)
else:
dateto = lst_dates[i_date + 1] - timedelta(days=1)
outname = "{0} {1} - {2}".format(name, datefrom.strftime('%Y/%m/%d'), dateto.strftime('%Y/%m/%d'))
curs_out.insertRow((polygon, row[1], name, outname))
The code will create the output column with the format from your example
Kind regards, Xander
PS: If the answer responds you question mark the post as answered at the post that answered your question. If it was helpful, mark it as helpful. This way other forum users will be able to find useful content. More on GeoNet can be found here: GeoNet Help
There is a point I don't really get; when the end date of a record (current date you want to process) is not equal to 2300, BUT it is higher than the current date, should it be removed from the output?
It seems more logic to me if for each unique date in the StartDate you use all the records for which the start date is before the current date and the end date is after the current date and use those records to create the polygon.
Anyway, I would probably use some Python code to:
Then there is still the issue of what to do with the attributes of the records you're merging...
If you're willing to post a piece of your data, I can have a look and see what code is needed.
Kind regards, Xander
Thanks for the reply Xander,
To answer your first question if the end year is not equal to 2300 but is higher than the current date, then it should be removed when it's end date is less than any of the start dates.
Could you tell me how I upload a file so you can get to it?
I can discuss the attributes once you have the file.
Thanks
Paul
ZIP the extract of your data (shapefile or FGBD), and use the "reply" option in this thread. In the editor in the upper right corner you will see a link "Use advanced editor", once in the advanced editor you will see in the lower right corner the possibility to attach a file.
Hi Paul,
I have included some Python code that created a new shapefile with the merged polygons. The attributes are still to be defined. In the code the processing date is assigned the the output date fields.
Prerequisites:
The reason to do this is to simplify the syntax of the where clause used in the script.
Configuration:
The script will:
import arcpy, os
from datetime import datetime
arcpy.env.overwriteOutput = True
# change path to datasource
fc = r"D:\Xander\GeoNet\MergePolsDate\Reserves_copy.shp"
fld_start = "DateFrom" # create date field and fill it with content of StartDate
fld_end = "DateTo" # create date field and fill it with content of EndDate
# output shape
fc_out = r"D:\Xander\GeoNet\MergePolsDate\Reserves_merged.shp"
# create empty output shapefile
ws_path, fc_out_name = os.path.split(fc_out)
arcpy.CreateFeatureclass_management(ws_path, fc_out_name,
"POLYGON", fc, "SAME_AS_TEMPLATE", "SAME_AS_TEMPLATE", fc)
flds = ("SHAPE@", fld_start, fld_end)
# create a unique list of dates from start date
lst_dates = list(set([row[0] for row in arcpy.da.SearchCursor(fc, (fld_start))]))
lst_dates.sort()
# insert cursor to store features to new featureclass
with arcpy.da.InsertCursor(fc_out, (flds)) as curs_out:
# loop through list of unique dates
for date in lst_dates:
# create an expression
expression = "{0} <= date '{2}' AND {1} >= date '{2}'".format(
arcpy.AddFieldDelimiters(fc, fld_start),
arcpy.AddFieldDelimiters(fc, fld_end), date)
i = 0
# create the searchcursor with the expression
with arcpy.da.SearchCursor(fc, flds, where_clause=expression) as curs:
for row in curs:
pol = row[0]
if i == 0:
# first polygon is just the polygon"
polygon = pol
else:
# next polygons are added with union
polygon = polygon.union(pol)
i += 1
# insert the feature into the output featureclass
# TODO: manage attributes
curs_out.insertRow((polygon, row[1], row[1]))
In case of any doubts, just post a reply.
Kind regards, Xander
Hi Xander,
That worked.
Just need to include the name and date attributes in the output.
What is being asked of me is to combine polygons into financial years, please see the example below.
Hope this makes sense.
Thank you for your time on this.
Paul
Hi Paul,
From your example I don't really understand the concept of a "financial year". To fill the EndDate columns with the value 31/21/2300 doesn't seem right, since polygons that expire before that date, but have an end date after the current processing date, are included too.
It is possible to add the name and create a new column "OutputName" that holds the name and a date range. What is not very clear to me is this:
Since the first record in the sample shapefile has an end date of 31/21/2300 it will be included in all the features.
Maybe you can explain a little more on what you are trying to achieve?
Hi Xander,
Apologies for this. Don't worry about the financial year, I can sort that out later.
Have a look at this example
So the first version of the reserve is live between 01/04/1962 until 06/06/1965 as on the 07/06/1965 the reserve then includes both row 2 and 3 and so on.
Hope that makes it easier.
Again thank you for your patience.
Paul
Hi Paul,
I think you could try something like this:
import arcpy, os
from datetime import datetime, timedelta
arcpy.env.overwriteOutput = True
# change path to datasource
fc = r"D:\Xander\GeoNet\MergePolsDate\Reserves_copy.shp"
fld_start = "DateFrom" # create date field and fill it with content of StartDate
fld_end = "DateTo" # create date field and fill it with content of EndDate
fld_name = "Name"
fld_outname = "OutputName" # output name
len_outname = 75
# output shape
fc_out = r"D:\Xander\GeoNet\MergePolsDate\Reserves_merged3.shp"
# create empty output shapefile
ws_path, fc_out_name = os.path.split(fc_out)
arcpy.CreateFeatureclass_management(ws_path, fc_out_name,
"POLYGON", fc, "SAME_AS_TEMPLATE", "SAME_AS_TEMPLATE", fc)
# add string field
arcpy.AddField_management(fc_out, fld_outname, "TEXT", "", "", len_outname)
flds = ("SHAPE@", fld_start, fld_end, fld_name)
flds_out = ("SHAPE@", fld_start, fld_name, fld_outname)
# create a unique list of dates from start date
lst_dates = list(set([row[0] for row in arcpy.da.SearchCursor(fc, (fld_start))]))
lst_dates.sort()
# insert cursor to store features to new featureclass
with arcpy.da.InsertCursor(fc_out, (flds_out)) as curs_out:
# loop through list of unique dates
for date in lst_dates:
# create an expression
expression = "{0} <= date '{2}' AND {1} >= date '{2}'".format(
arcpy.AddFieldDelimiters(fc, fld_start),
arcpy.AddFieldDelimiters(fc, fld_end), date)
i = 0
# create the searchcursor with the expression
with arcpy.da.SearchCursor(fc, flds, where_clause=expression) as curs:
for row in curs:
pol = row[0]
name = row[3] # take name of first polygon
if i == 0:
# first polygon is just the polygon"
polygon = pol
else:
# next polygons are added with union
polygon = polygon.union(pol)
i += 1
# insert the feature into the output featureclass
datefrom = date
i_date = lst_dates.index(date)
if i_date + 1 > len(lst_dates) - 1:
dateto = ""
outname = "{0} {1} - {2}".format(name, datefrom.strftime('%Y/%m/%d'), dateto)
else:
dateto = lst_dates[i_date + 1] - timedelta(days=1)
outname = "{0} {1} - {2}".format(name, datefrom.strftime('%Y/%m/%d'), dateto.strftime('%Y/%m/%d'))
curs_out.insertRow((polygon, row[1], name, outname))
The code will create the output column with the format from your example
Kind regards, Xander
PS: If the answer responds you question mark the post as answered at the post that answered your question. If it was helpful, mark it as helpful. This way other forum users will be able to find useful content. More on GeoNet can be found here: GeoNet Help