· 8 years ago · May 30, 2018, 03:40 AM
1import sys, os, datetime, time, shutil, traceback
2import argparse
3import json
4import arcpy
5from util import Util
6
7COVERAGE_TYPES = ["FULL", "REDUCED"]
8VALID_NAME_CHARS = '-_() abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
9
10# class for coverage calculation. Common in python toolbox and python code
11class CoverageCalculation:
12 def __init__(self, config_parser):
13 # store needed input params
14 self.fc_A = config_parser.get('workspace', 'fc_A')
15 self.fc_A_pkey = config_parser.get('workspace', 'fc_A_pkey')
16 self.fc_B = config_parser.get('workspace', 'fc_B')
17 self.fc_B_pkey = config_parser.get('workspace', 'fc_A_pkey')
18 self.fc_C = config_parser.get('workspace', 'fc_C')
19 self.fc_C_pkey = config_parser.get('workspace', 'fc_A_pkey')
20 self.ws_coverage_area = config_parser.get('workspace', 'ws_coverage')
21 self.fc_coverage_area = config_parser.get('workspace', 'fc_coveragearea')
22 self.tbl_cellnames = config_parser.get('workspace', 'tbl_cellnames')
23 self.fc_landmass = config_parser.get('workspace', 'fc_landmass')
24
25 def validate_json(self, json_file):
26 is_valid = True
27 json_input = None
28 try:
29 if (os.path.exists(json_file)):
30 with open(json_file) as fp:
31 json_input = json.load(fp)
32 calc_type = json_input.get("calculation_type")
33 if (calc_type is None or calc_type == ""):
34 Util.log("calculation_type must be set.", Util.LOG_LEVEL_ERROR, True, True)
35 is_valid = False
36 elif (not json_input["calculation_type"] in COVERAGE_TYPES):
37 Util.log("calculation_type must be %s." %(" or ".join(COVERAGE_TYPES)), Util.LOG_LEVEL_ERROR, True, True)
38 is_valid = False
39 area_name = json_input.get("area_name")
40 if (area_name is None or len(area_name) == 0):
41 Util.log("area_name must be set.", Util.LOG_LEVEL_ERROR, True, True)
42 is_valid = False
43 else:
44 VALID_NAME_CHARS = '-_() abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
45 valid_chars = "".join(char for char in area_name if char in VALID_NAME_CHARS)
46 if (len(valid_chars) != len(area_name)):
47 Util.log("area_name can only contain %s." % (VALID_NAME_CHARS), Util.LOG_LEVEL_ERROR,
48 True, True)
49 is_valid = False
50 cell_names = json_input.get("cell_names")
51 if (cell_names is None or len(cell_names) == 0):
52 Util.log("cell_names must be set.", Util.LOG_LEVEL_ERROR, True, True)
53 is_valid = False
54
55 else:
56 Util.log("Input json file %s does not exist or is invalid." % (json_file),
57 Util.LOG_LEVEL_ERROR, True, True)
58 is_valid = False
59 if (is_valid):
60 return json_input
61 else:
62 return None
63 except Exception as ex:
64 Util.log("Error validating input json file %s: %s" %(json_file, ex.message), Util.LOG_LEVEL_ERROR, True, True)
65 return None
66
67 # validate input feature classes and output fc/table
68 # also validate input parameters
69 def validate_input(self, coverage_type, area_name, cell_names):
70 ret_valid = True
71 fc_total_coverage = os.path.join(self.ws_coverage_area, self.fc_coverage_area)
72 tbl_coverage_cells = os.path.join(self.ws_coverage_area, self.tbl_cellnames)
73 arc_objects = [self.fc_A, self.fc_B, self.fc_C, fc_total_coverage, tbl_coverage_cells, self.fc_landmass]
74 for arc_object in arc_objects:
75 if not arcpy.Exists(arc_object):
76 Util.log("Feature/Table %s does not exist or is invalid." % (arc_object), Util.LOG_LEVEL_ERROR, True, True)
77 ret_valid = False
78 for fc_polygon in [self.fc_A, self.fc_B, self.fc_C, self.fc_landmass]:
79 if arcpy.Exists(fc_polygon):
80 desc = arcpy.Describe(fc_polygon)
81 if (desc.datasetType != "FeatureClass" or desc.shapeType != "Polygon"):
82 Util.log("Feature class %s is not a polygon" % (fc_polygon), Util.LOG_LEVEL_ERROR, True, True)
83 ret_valid = False
84
85 if (area_name is None or len(area_name) == 0):
86 Util.log("Area name must be set.", Util.LOG_LEVEL_ERROR, True, True)
87 ret_valid = False
88 else:
89
90 valid_chars = "".join(char for char in area_name if char in VALID_NAME_CHARS)
91 if (len(valid_chars) != len(area_name)):
92 Util.log("Area name can only contain %s." %(VALID_NAME_CHARS), Util.LOG_LEVEL_ERROR, True, True)
93 ret_valid = False
94 if (cell_names is None):
95 Util.log("Cell name should have at least one entry", Util.LOG_LEVEL_ERROR, True, True)
96 ret_valid = False
97
98 return ret_valid
99
100 # delete old coverage area by same name
101 def delete_coverage_area(self, area_name):
102 fc_total_coverage = os.path.join(self.ws_coverage_area, self.fc_coverage_area)
103 tbl_coverage_cells = os.path.join(self.ws_coverage_area, self.tbl_cellnames)
104 delete_clause = "AreaName='%s'" %(area_name)
105
106 # delete from table for cell names
107 delete_fields = ["AreaName", "CellName", "OID@"]
108 with arcpy.da.UpdateCursor(in_table=tbl_coverage_cells, field_names=delete_fields, where_clause=delete_clause) as del_cursor:
109 for row in del_cursor:
110 del_cursor.deleteRow()
111
112 # delete from FC
113 delete_fields = ["AreaName", "OID@"]
114 with arcpy.da.UpdateCursor(in_table=fc_total_coverage, field_names=delete_fields, where_clause=delete_clause) as del_cursor:
115 for row in del_cursor:
116 del_cursor.deleteRow()
117
118 # save coverage area, only used for Total Coverage and not Reduced Coverage
119 def save_coverage_area(self, area_name, cell_names, geom_polygon):
120 area_size = geom_polygon.area / 1000000.0
121 fc_total_coverage = os.path.join(self.ws_coverage_area, self.fc_coverage_area)
122 tbl_coverage_cells = os.path.join(self.ws_coverage_area, self.tbl_cellnames)
123
124 insert_fields = ["AreaName", "AreaSqKm", "DateCalculated", "SHAPE@"]
125 insert_cursor = arcpy.da.InsertCursor(in_table=fc_total_coverage,
126 field_names=insert_fields)
127
128 oid = insert_cursor.insertRow((area_name, area_size, datetime.datetime.now(), geom_polygon))
129 del insert_cursor
130 insert_fields = ["AreaName", "CellName"]
131 insert_cursor = arcpy.da.InsertCursor(in_table=tbl_coverage_cells,
132 field_names=insert_fields)
133
134 for cell_name in cell_names:
135 oid = insert_cursor.insertRow((area_name, cell_name))
136
137 del insert_cursor
138
139 # perform validation for reduced coverage area calculation
140 # checks if total coverage area name already exists and it has atleast one cell
141 def validate_total_area(self, coverage_type, area_name, cell_names):
142 has_area = False
143 try:
144 fc_total_coverage = os.path.join(self.ws_coverage_area, self.fc_coverage_area)
145 tbl_coverage_cells = os.path.join(self.ws_coverage_area, self.tbl_cellnames)
146 fields = ["OID@"]
147 where_clause = "AreaName='%s'" % (area_name)
148
149 with arcpy.da.SearchCursor(in_table=fc_total_coverage, field_names=fields, where_clause=where_clause) as cursor:
150 for row in cursor:
151 has_area = True
152 break
153 if (has_area == False):
154 Util.log("Input Area Name does not exist in Full Coverage feature class. Reduced coverage cannot be calculated.",
155 Util.LOG_LEVEL_WARNING, True, True)
156 else:
157 has_area = False
158 with arcpy.da.SearchCursor(in_table=tbl_coverage_cells, field_names=fields, where_clause=where_clause) as cursor:
159 for row in cursor:
160 has_area = True
161 break
162 if (has_area == False):
163 Util.log("Input Area Name does not have any cells. Reduced coverage cannot be calculated.",
164 Util.LOG_LEVEL_WARNING, True, True)
165
166 except Exception as e:
167 Util.log("Error validating input area: " + e.message, Util.LOG_LEVEL_ERROR, True, True)
168 has_area = False
169
170 return has_area
171
172 # get cell names for total coverage area excluding the ones to be excluded
173 def get_area_included_cell_names(self, area_name, cell_names_excluded):
174 cells_included = []
175 try:
176 fc_total_coverage = os.path.join(self.ws_coverage_area, self.fc_coverage_area)
177 tbl_coverage_cells = os.path.join(self.ws_coverage_area, self.tbl_cellnames)
178 where_clause = "AreaName='%s'" % (area_name)
179 fields = ["CellName"]
180 with arcpy.da.SearchCursor(in_table=tbl_coverage_cells, field_names=fields,
181 where_clause=where_clause) as cursor:
182 for row in cursor:
183 if (not row[0] is None):
184 cell_name = str(row[0]).lower()
185 if cell_name not in [x.lower() for x in cell_names_excluded]:
186 cells_included.append(str(row[0]))
187 except Exception as ex:
188 Util.log("Fatal error: " + ex.message, Util.LOG_LEVEL_ERROR, True, True)
189 exc_type, exc_value, exc_traceback = sys.exc_info()
190 Util.log(repr(traceback.format_exception(exc_type, exc_value, exc_traceback)), Util.LOG_LEVEL_ERROR, False,
191 True)
192
193 return cells_included
194
195 # get geometry for select_layer
196 # select_layer is a result of dissolve output and is expected to have only one row
197 def get_polygon_geometry(self, select_layer):
198 try:
199 fields = ["SHAPE@"]
200 with arcpy.da.SearchCursor(in_table=select_layer, field_names=fields) as cursor:
201 for row in cursor:
202 return row[0]
203 break
204 except Exception as ex:
205 Util.log("Fatal error: " + ex.message, Util.LOG_LEVEL_ERROR, True, True)
206 exc_type, exc_value, exc_traceback = sys.exc_info()
207 Util.log(repr(traceback.format_exception(exc_type, exc_value, exc_traceback)), Util.LOG_LEVEL_ERROR, False,
208 True)
209 return None
210
211 # get spatial ref for coverage dataset (output)
212 def get_spatial_ref(self):
213 fc_total_coverage = os.path.join(self.ws_coverage_area, self.fc_coverage_area)
214 desc = arcpy.Describe(fc_total_coverage)
215 return desc.spatialReference
216
217 # calculate area for cells. Can be excluded cells or included cells.
218 def get_geom_for_cells(self, full_fgdb_path, temp_cell_layer, area_name):
219 projected_geom = None
220 try:
221 result_count = arcpy.GetCount_management(temp_cell_layer)
222 count_cells_sel = int(result_count.getOutput(0))
223 if (count_cells_sel == 0):
224 Util.log("No cell names matched input coverage datasets.", Util.LOG_LEVEL_WARNING,
225 True, True)
226 elif (count_cells_sel == 1):
227 Util.log("One cell matched input coverage datasets.", Util.LOG_LEVEL_DEBUG,
228 False, True)
229 search_fields = ["SHAPE@"]
230 sr = arcpy.SpatialReference("Web Mercator Auxillary Sphere")
231 with arcpy.da.SearchCursor(in_table=temp_cell_layer, field_names=search_fields) as cursor:
232 for row in cursor:
233 geometry_dissolved = row[0]
234 # project geometry to Albers and get area in sq km
235 projected_geom = geometry_dissolved.projectAs(spatial_reference=sr)
236 area_size = projected_geom.area / 1000000.0
237 Util.log("Area=%.4f" %(area_size), Util.LOG_LEVEL_DEBUG, False, True)
238 break
239 else:
240 Util.log("Multiple cells matched input coverage datasets.", Util.LOG_LEVEL_DEBUG,
241 False, True)
242 # more than one geometry, dissolve it
243 # dissolve all input polygons
244 dissolve_layer = r"IN_MEMORY\lyr_dissolve"
245 if (arcpy.Exists(dissolve_layer)):
246 arcpy.Delete_management(dissolve_layer)
247 arcpy.MakeFeatureLayer_management(temp_cell_layer, dissolve_layer)
248
249 temp_dissolve_layer = os.path.join(full_fgdb_path, "Dissolved_For_%s" % (area_name))
250 Util.log("Building '%s' ..." % (temp_dissolve_layer), Util.LOG_LEVEL_DEBUG, False, True)
251 arcpy.Dissolve_management(dissolve_layer, temp_dissolve_layer)
252
253 # clip dissolved polygon to Land Mass
254 temp_clip_layer = os.path.join(full_fgdb_path, "Landmass_For_%s" % (area_name))
255 Util.log("Building '%s' ..." % (temp_clip_layer), Util.LOG_LEVEL_DEBUG, False, True)
256 arcpy.Clip_analysis(temp_dissolve_layer, self.fc_landmass, temp_clip_layer)
257
258 # now get total area
259 Util.log("Calculating area for features in '%s' ..." % (temp_clip_layer), Util.LOG_LEVEL_DEBUG, False, True)
260 search_fields = ["SHAPE@"]
261
262 sr = arcpy.SpatialReference("Web Mercator Auxillary Sphere")
263 with arcpy.da.SearchCursor(in_table=temp_clip_layer, field_names=search_fields) as cursor:
264 for row in cursor:
265 geometry_dissolved = row[0]
266 # project geometry to Web Mercator and get area in sq km
267 projected_geom = geometry_dissolved.projectAs(spatial_reference=sr)
268 break
269
270 if (arcpy.Exists(dissolve_layer)):
271 arcpy.Delete_management(dissolve_layer)
272
273 except Exception as e:
274 Util.log("Fatal error: " + e.message, Util.LOG_LEVEL_ERROR, True, True)
275 exc_type, exc_value, exc_traceback = sys.exc_info()
276 Util.log(repr(traceback.format_exception(exc_type, exc_value, exc_traceback)), Util.LOG_LEVEL_ERROR, False,
277 True)
278
279 return projected_geom
280
281 # perform outage area calculation using cell names to be excluded.
282 # Excluded cell polygons may overlap with included cell polygons
283 def perform_reduced_area_calc(self, temp_path, area_name, cells_excluded):
284 area_size = -1
285 try:
286 localtime = time.localtime()
287 fgdb_name = time.strftime("%d%m%y_%H%M%S", localtime)
288 full_fgdb_path = os.path.join(temp_path, fgdb_name + ".gdb")
289
290 # delete this FGDB, just in case
291 if (arcpy.Exists(full_fgdb_path)):
292 shutil.rmtree(full_fgdb_path)
293
294 arcpy.CreateFileGDB_management(temp_path, fgdb_name)
295 sr = self.get_spatial_ref()
296 out_name = "clipped_exclude_areas"
297 arcpy.CreateFeatureclass_management(out_path=full_fgdb_path, out_name=out_name, spatial_reference=sr)
298 fc_out_name = os.path.join(full_fgdb_path, out_name)
299 arcpy.AddField_management(in_table=fc_out_name, field_name="CellName", field_type="TEXT", field_length=50)
300
301 cells_included_for_area = self.get_area_included_cell_names(area_name, cells_excluded)
302
303 has_fgdb = True
304 # create in-memory layer for all cell polygon features to be included removing to be excluded
305 for fc_polygon in [self.fc_A, self.fc_B, self.fc_C]:
306 Util.log("Creating feature layer for '%s' using included cell names..." %(fc_polygon), Util.LOG_LEVEL_DEBUG,
307 False, True)
308 fc_pkey = "TX_ID"
309 include_layer = r"IN_MEMORY\A"
310 if (fc_polygon == self.fc_A):
311 fc_pkey = self.fc_A_pkey
312 include_layer = r"IN_MEMORY\A"
313 elif (fc_polygon == self.fc_B):
314 fc_pkey = self.fc_B_pkey
315 include_layer = r"IN_MEMORY\B"
316 elif (fc_polygon == self.fc_C):
317 fc_pkey = self.fc_C_pkey
318 include_layer = r"IN_MEMORY\C"
319 where_include = "%s In ('%s')" % (fc_pkey, "','".join(cells_included_for_area))
320 if (arcpy.Exists(include_layer)):
321 arcpy.Delete_management(include_layer)
322 arcpy.MakeFeatureLayer_management(in_features=fc_polygon, out_layer=include_layer, where_clause=where_include)
323
324 # loop through all cells excluded and clip overlapping areas with cells included
325 for cell_excluded in cells_excluded:
326 Util.log("Processing excluded cell '%s'..." % (cell_excluded),
327 Util.LOG_LEVEL_DEBUG, False, True)
328
329 select_layer = r"IN_MEMORY\%s" %(cell_excluded)
330 if (arcpy.Exists(select_layer)):
331 arcpy.Delete_management(select_layer)
332
333 # loop through input polygons excluded and clip by areas included
334 for fc_polygon in [self.fc_A, self.fc_B, self.fc_C]:
335 fc_pkey = "TX_ID"
336 if (fc_polygon == self.fc_A):
337 fc_pkey = self.fc_A_pkey
338 include_layer = r"IN_MEMORY\A"
339 elif (fc_polygon == self.fc_B):
340 fc_pkey = self.fc_B_pkey
341 include_layer = r"IN_MEMORY\B"
342 elif (fc_polygon == self.fc_C):
343 fc_pkey = self.fc_C_pkey
344 include_layer = r"IN_MEMORY\C"
345
346 where_exclude = "%s = '%s'" % (fc_pkey, cell_excluded)
347 Util.log("Making feature layer for '%s' using %s..." % (fc_polygon, where_exclude),
348 Util.LOG_LEVEL_DEBUG, False, True)
349
350 arcpy.MakeFeatureLayer_management(fc_polygon, select_layer, where_exclude)
351 geometry = self.get_polygon_geometry(select_layer)
352
353 if (not geometry is None):
354 Util.log("Selecting geometries intersecting with %s..." % (cell_excluded),
355 Util.LOG_LEVEL_DEBUG, False, True)
356 # TODO: this could potentially take long time, can have memory faults on very large dataset
357 arcpy.SelectLayerByLocation_management(in_layer=include_layer, overlap_type="INTERSECT",
358 select_features=select_layer, search_distance=0,
359 selection_type="NEW_SELECTION")
360 fields = ["SHAPE@", fc_pkey]
361 with arcpy.da.SearchCursor(in_table=include_layer, field_names=fields) as cursor:
362 for row in cursor:
363 Util.log("Intersecting cell %s with excluded cell %s..." % (row[1], cell_excluded),
364 Util.LOG_LEVEL_DEBUG, False, True)
365 if (not row[0] is None):
366 #clip the excluded geometry that overlaps with included geometry leaving only outage area
367 # we keep clipping until all intersecting polygons are through
368 geometry = geometry.difference(row[0])
369
370 # if any area is left from the cell excluded that is not covered by any cell included
371 # add this geometry
372 # we could go in-memory geometry but we need to debug occasionally
373 if (geometry.area > 0):
374 insert_fields = ["CellName", "SHAPE@"]
375 insert_cursor = arcpy.da.InsertCursor(in_table=fc_out_name,
376 field_names=insert_fields)
377 insert_cursor.insertRow((cell_excluded, geometry))
378 del insert_cursor
379
380 if (arcpy.Exists(select_layer)):
381 arcpy.Delete_management(select_layer)
382
383 # now dissolve all polygons and project to ALBERS, this will be the outage area
384 geom_proj_albers = self.get_geom_for_cells(full_fgdb_path=full_fgdb_path,
385 temp_cell_layer=fc_out_name,
386 area_name=area_name)
387
388 if (not geom_proj_albers is None):
389 area_size = geom_proj_albers.area / 1000000
390 area_size = float("%.4f" %(area_size))
391
392 #clean-up
393 in_mem_layers = [r"IN_MEMORY\A", r"IN_MEMORY\B", r"IN_MEMORY\C"]
394 for include_layer in in_mem_layers:
395 if (arcpy.Exists(include_layer)):
396 arcpy.Delete_management(include_layer)
397
398 try:
399 if (arcpy.Exists(full_fgdb_path)):
400 shutil.rmtree(full_fgdb_path)
401 except Exception as e:
402 Util.log("Error removing temp FGDB: " + e.message, Util.LOG_LEVEL_WARNING, True, True)
403
404 return area_size
405
406 except Exception as e:
407 Util.log("Fatal error: " + e.message, Util.LOG_LEVEL_ERROR, True, True)
408 exc_type, exc_value, exc_traceback = sys.exc_info()
409 Util.log(repr(traceback.format_exception(exc_type, exc_value, exc_traceback)), Util.LOG_LEVEL_ERROR, False,
410 True)
411 return area_size
412
413 return area_size
414
415 # perform full area calculation
416 def perform_full_calc(self, temp_path, area_name, cell_names):
417 has_fgdb = False
418 area_size = -1
419
420 try:
421 localtime = time.localtime()
422 fgdb_name = time.strftime("%d%m%y_%H%M%S", localtime)
423 full_fgdb_path = os.path.join(temp_path, fgdb_name + ".gdb")
424
425 # delete this FGDB, just in case
426 if (arcpy.Exists(full_fgdb_path)):
427 shutil.rmtree(full_fgdb_path)
428
429 arcpy.CreateFileGDB_management(temp_path, fgdb_name)
430 has_fgdb = True
431
432 temp_cell_layer = os.path.join(full_fgdb_path, "Merged_For_%s" %(area_name))
433 Util.log("Building '%s' ..." %(temp_cell_layer), Util.LOG_LEVEL_DEBUG, False, True)
434 first = True
435 # loop through input polygons and combine needed polygons
436 for fc_polygon in [self.fc_A, self.fc_B, self.fc_C]:
437
438 selection_layer = r"IN_MEMORY\lyr_merge"
439 if (arcpy.Exists(selection_layer)):
440 arcpy.Delete_management(selection_layer)
441
442 fc_pkey = "TX_ID"
443 if (fc_polygon == self.fc_A):
444 fc_pkey = self.fc_A_pkey
445 elif (fc_polygon == self.fc_B):
446 fc_pkey = self.fc_B_pkey
447 elif (fc_polygon == self.fc_C):
448 fc_pkey = self.fc_C_pkey
449
450 # TODO: Not sure what is the limit on where clause
451 # There could be thousands of cell names
452 where_clause = "%s in ('%s')" %(fc_pkey, "','".join(cell_names))
453 Util.log("Making feature layer for '%s' using %s..." % (fc_polygon, where_clause),
454 Util.LOG_LEVEL_DEBUG, False, True)
455 arcpy.MakeFeatureLayer_management(fc_polygon, selection_layer, where_clause)
456 if (first):
457 first = False
458 arcpy.Merge_management(selection_layer, temp_cell_layer)
459 else:
460 arcpy.Append_management(selection_layer, temp_cell_layer, "NO_TEST")
461
462 result_count = arcpy.GetCount_management(temp_cell_layer)
463 count_cells_sel = int(result_count.getOutput(0))
464 if (count_cells_sel == 0):
465 Util.log("No cell names matched input coverage datasets.", Util.LOG_LEVEL_WARNING,
466 True, True)
467 area_size = -1
468 else:
469 geom_proj_albers = self.get_geom_for_cells(full_fgdb_path=full_fgdb_path, temp_cell_layer=temp_cell_layer,
470 area_name=area_name)
471 if (not geom_proj_albers is None):
472 area_size = geom_proj_albers.area / 1000000
473 area_size = float("%.4f" % (area_size))
474 Util.log("Saving polygon for full coverage area '%s' ..." %(area_name), Util.LOG_LEVEL_DEBUG,
475 False, True)
476 # delete this coverage just in case
477 self.delete_coverage_area(area_name)
478 self.save_coverage_area(area_name, cell_names, geom_proj_albers)
479 else:
480 Util.log("Null polygon for full coverage area '%s'" % (area_name), Util.LOG_LEVEL_WARNING,
481 True, True)
482 #clean-up
483 try:
484 if (arcpy.Exists(full_fgdb_path)):
485 shutil.rmtree(full_fgdb_path)
486 except Exception as e:
487 Util.log("Error removing temp FGDB: " + e.message, Util.LOG_LEVEL_INFO, True, True)
488
489 return area_size
490
491 except Exception as e:
492 Util.log("Fatal error: " + e.message, Util.LOG_LEVEL_ERROR, True, True)
493 exc_type, exc_value, exc_traceback = sys.exc_info()
494 Util.log(repr(traceback.format_exception(exc_type, exc_value, exc_traceback)), Util.LOG_LEVEL_ERROR, False,
495 True)
496 return area_size
497
498
499# entry point for tool execution using python.exe
500if __name__ == "__main__":
501 has_error = False
502 try:
503 parser = argparse.ArgumentParser(description='Calculate Coverage Area')
504 parser.add_argument('-i', dest='input_json_file', action='store', metavar='json_file',
505 help=r'input json file path and name e.g. C:\Tools\CoverageCells.json', required=True)
506 args = parser.parse_args()
507 json_file_path_name = args.input_json_file
508
509 config_parser = Util.loadConfig(config_file_name='Config.INI')
510 log_path = config_parser.get('logging', 'logroot')
511 logging_level = config_parser.getint("logging", "logginglevel")
512 log_name = "CoverageCalculation"
513 Util.startLogging(log_path, log_name, logging_level)
514 try:
515 localtime = time.localtime()
516 localtime_start = time.strftime("%d%m%y_%H%M%S", localtime)
517
518 coverage_calc = CoverageCalculation(config_parser)
519 json_input = coverage_calc.validate_json(json_file_path_name)
520 if (json_input is None):
521 Util.log("Invalid input json file %s. Processing terminated " %(json_file_path_name), Util.LOG_LEVEL_ERROR, True, True)
522 else:
523 try:
524 calc_type = json_input["calculation_type"]
525 area_name = json_input["area_name"]
526 cell_names = json_input["cell_names"]
527 Util.log("Starting %s coverage calculation..." %(calc_type), Util.LOG_LEVEL_DEBUG, False, True)
528 area_km = 0
529 status = "success"
530 if (calc_type == "FULL"):
531 area_km = coverage_calc.perform_full_calc(log_path, area_name, cell_names)
532 else:
533 area_km = coverage_calc.perform_reduced_area_calc(log_path, area_name, cell_names)
534
535 if (area_km < 0):
536 status = "failure"
537 localtime = time.localtime()
538 localtime_end = time.strftime("%d%m%y_%H%M%S", localtime)
539
540 json_input["calculated_area"] = area_km
541 json_input["status"] = status
542
543 except Exception as e:
544 Util.log("Fatal error: " + e.message, Util.LOG_LEVEL_ERROR, True, True)
545 exc_type, exc_value, exc_traceback = sys.exc_info()
546 Util.log(repr(traceback.format_exception(exc_type, exc_value, exc_traceback)), Util.LOG_LEVEL_ERROR,
547 True, True)
548 json_input["calculated_area"] = -1
549 json_input["status"] = "failure"
550 #write output
551 json_input["start_at"] = localtime_start
552 json_input["end_at"] = localtime_end
553 json_dir, json_filename = os.path.split(json_file_path_name)
554 json_file, json_extn = os.path.splitext(json_filename)
555 json_file_path_name_output = os.path.join(json_dir, "%s_output%s" %(json_file, json_extn))
556 with open(json_file_path_name_output, "w") as fp_write:
557 json.dump(obj=json_input, fp=fp_write, indent=4, sort_keys=True )
558 Util.log("%s coverage calculation completed." % (calc_type), Util.LOG_LEVEL_DEBUG, False, True)
559 except Exception as e:
560 Util.log("Fatal error: " + e.message, Util.LOG_LEVEL_ERROR, True, True)
561 exc_type, exc_value, exc_traceback = sys.exc_info()
562 Util.log(repr(traceback.format_exception(exc_type, exc_value, exc_traceback)), Util.LOG_LEVEL_ERROR, False,
563 True)
564 except Exception as ex:
565 print("Fatal error: " + ex.message)
566 exc_type, exc_value, exc_traceback = sys.exc_info()
567 error_msg = repr(traceback.format_exception(exc_type, exc_value, exc_traceback))
568 print(error_msg)