def __init__(self, ss, s57, cs, use_valsous, use_contours=False, detect_deeps=False, multiplier=1.0, sounding_unit=sounding_units['feet'], progress=None): super(TriangleRuleV2, self).__init__(ss=ss, s57=s57, cs=cs, sounding_unit=sounding_unit, progress=progress) self.type = triangle_algos["TRIANGLE_RULE_v2"] self.use_valsous = use_valsous self.use_contours = use_contours self.detect_deeps = detect_deeps self.multiplier = multiplier self.gd = Geodesy() self.all_ss = self.ss.rec10s if self.s57 is None: self.all_s57 = list() else: self.all_s57 = self.s57.rec10s if self.cs is None: self.all_cs = list() else: self.all_cs = self.cs.rec10s self.points2d = None self.points3d = None self.delaunay = None self.edges_a = None self.edges_b = None self.triangles = None self.bad_triangles = None self.good_triangles = None
def write(cls, feature_list, path): if not os.path.exists(os.path.dirname(path)): raise RuntimeError("the passed path does not exist: %s" % path) path = Helper.truncate_too_long(path) if not isinstance(feature_list, list): raise RuntimeError("the passed parameter as feature_list is not a list: %s" % type(feature_list)) # generating header header = str() header += "[SVP_VERSION_2]\n" header += "%s\n" % path # generating body body = str() date_string = "%s" % datetime.now().strftime("%Y-%j %H:%M:%S") for m, ft in enumerate(feature_list): dd_lon = ft[0] dd_lat = ft[1] lon_d, lon_m, lon_s = Gd.dd2dms(dd_lon) lat_d, lat_m, lat_s = Gd.dd2dms(dd_lat) position_string = "{0:02d}:{1:02d}:{2:05.2f} {3:02d}:{4:02d}:{5:05.2f}".format(int(lat_d), int(lat_m), lat_s, int(lon_d), int(lon_m), lon_s) body += "Section " + date_string + " " + position_string + " Created by hyo2.qc\n" body += " 1.00 1500.00\n" body += " 15.00 1500.00\n" # open the file for writing with open(path, 'w') as fid: fid.write(header + body)
def generate_ascii(self, output_file): """All collected SBDARE objects are printed to an ASCII file per HTD 2013-3""" # create output file fod = open(output_file, 'w') # add header fod.write( 'Latitude;Longitude;Observed time;Colour;Nature of surface - qualifying terms;Nature of surface;' 'Remarks;Source date;Source indication\n') # for each SBDARE, write a row for feature in self.sbdare_features: # retrieve position lat = Geodesy.dd2dms(feature.centroid.y) lon = Geodesy.dd2dms(feature.centroid.x) lat_str = "%02.0f-%02.0f-%05.2f%s" % (abs(lat[0]), lat[1], lat[2], ("N" if (lat[0] > 0) else "S")) lon_str = "%03.0f-%02.0f-%05.2f%s" % (abs(lon[0]), lon[1], lon[2], ("E" if (lon[0] > 0) else "W")) # print(lat_str, lon_str) # retrieve attribute information observed_time = str() colour = str() natqua = str() natsur = str() remarks = str() sordat = str() sorind = str() for attribute in feature.attributes: if attribute.acronym == 'obstim': observed_time = attribute.value elif attribute.acronym == 'COLOUR': colour = attribute.value elif attribute.acronym == 'NATQUA': natqua = attribute.value elif attribute.acronym == 'NATSUR': natsur = attribute.value elif attribute.acronym == 'remrks': remarks = attribute.value elif attribute.acronym == 'SORDAT': sordat = attribute.value elif attribute.acronym == 'SORIND': sorind = attribute.value # actually write the SBDARE row fod.write("%s;%s;%s;%s;%s;%s;%s;%s;%s\n" % (lat_str, lon_str, observed_time, colour, natqua, natsur, remarks, sordat, sorind)) fod.close() return True
def geotag_jpeg(cls, img_path, lat, lon): exif_dict = piexif.load(img_path) # for key in exif_dict: # logger.debug("%s: %s" % (key, exif_dict[key],)) base = 10000 lat = Geodesy.dd2dms(lat) # logger.debug("lat: %s" % (lat, )) lon = Geodesy.dd2dms(lon) # logger.debug("lon: %s" % (lon,)) exif_dict["GPS"].clear() exif_dict["GPS"][piexif.GPSIFD.GPSVersionID] = (2, 3, 0, 0) if lat[0] > 0: exif_dict["GPS"][piexif.GPSIFD.GPSLatitudeRef] = "N" else: exif_dict["GPS"][piexif.GPSIFD.GPSLatitudeRef] = "S" exif_dict["GPS"][piexif.GPSIFD.GPSLatitude] = ((abs(int( lat[0])), 1), (int(lat[1]), 1), (int(lat[2] * base), base)) if lon[0] > 0: exif_dict["GPS"][piexif.GPSIFD.GPSLongitudeRef] = "E" else: exif_dict["GPS"][piexif.GPSIFD.GPSLongitudeRef] = "W" exif_dict["GPS"][piexif.GPSIFD.GPSLongitude] = ((abs(int( lon[0])), 1), (int(lon[1]), 1), (int(lon[2] * base), base)) exif_dict["GPS"][piexif.GPSIFD.GPSMapDatum] = "WGS-84" # remove for k in [ piexif.GPSIFD.GPSAltitudeRef, piexif.GPSIFD.GPSAltitude, piexif.GPSIFD.GPSSatellites, piexif.GPSIFD.GPSStatus, piexif.GPSIFD.GPSMeasureMode ]: if k in exif_dict["GPS"].keys(): del exif_dict["GPS"][k] exif_bytes = piexif.dump(exif_dict) im = Image.open(img_path) im.save(img_path, exif=exif_bytes)
def generate_output(self, output_folder, output_name): logger.debug("do EXIF: %s" % self.do_exif) # create ascii file self.output_ascii = os.path.join(output_folder, "%s.ascii" % output_name) ascii_fod = open(self.output_ascii, 'w') ascii_fod.write('Latitude;Longitude;Observed time;Colour;Nature of surface - qualifying terms;' 'Nature of surface;Remarks;Source date;Source indication;Images;' 'CMECS Substrate Name;CMECS Substrate Code;' 'CMECS Co-occurring Element 1 Name;CMECS Co-occurring Element 1 Code;' 'CMECS Co-occurring Element 2 Name;CMECS Co-occurring Element 2 Code\n') # create output folder self.cmecs_output_folder = os.path.join(output_folder, output_name) if not os.path.exists(self.cmecs_output_folder): os.mkdir(self.cmecs_output_folder) # create 'Images' output folder self.images_output_folder = os.path.join(self.cmecs_output_folder, "Images") if not os.path.exists(self.images_output_folder): os.mkdir(self.images_output_folder) # create shapefile self.output_shp = os.path.join(self.cmecs_output_folder, output_name) GdalAux() try: ds = GdalAux.create_ogr_data_source(ogr_format=GdalAux.ogr_formats['ESRI Shapefile'], output_path=self.output_shp) lyr = self._create_ogr_point_lyr_and_fields(ds) except RuntimeError as e: logger.error("%s" % e) return False # populate for idx, feature in enumerate(self.sbdare_features): # create OGR feature ft = ogr.Feature(lyr.GetLayerDefn()) # retrieve position for ASCII format lat = Geodesy.dd2dms(feature.centroid.y) lon = Geodesy.dd2dms(feature.centroid.x) lat_str = "%02.0f-%02.0f-%05.2f%s" % (abs(lat[0]), lat[1], lat[2], ("N" if (lat[0] > 0) else "S")) lon_str = "%03.0f-%02.0f-%05.2f%s" % (abs(lon[0]), lon[1], lon[2], ("E" if (lon[0] > 0) else "W")) # print(lat_str, lon_str) # retrieve position for shapefile format pt = ogr.Geometry(ogr.wkbPoint) pt.SetPoint(0, feature.centroid.x, feature.centroid.y) try: ft.SetGeometry(pt) except Exception as e: RuntimeError("%s > #%d pt: %s, %s" % (e, idx, feature.centroid.x, feature.centroid.y)) info = self._retrieve_info(feature=feature, feature_idx=idx) info = self._calc_cmecs(info=info, feature_idx=idx) # for each SBDARE, write a row in the ASCII file ascii_fod.write("%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s;%s\n" % (lat_str, lon_str, info.observed_time, info.colour, info.natqua, info.natsur, info.remrks, info.sordat, info.sorind, info.images, info.c_subn, info.c_subc, info.c_cen1, info.c_cec1, info.c_cen2, info.c_cec2)) # actually write the feature in the shapefile self._write_shape_attributes(ft, info) if lyr.CreateFeature(ft) != 0: raise RuntimeError("Unable to create feature") ft.Destroy() # finalize ASCII file ascii_fod.close() return self._finalize_generate_output(root_folder=output_folder, base_folder=output_name, remove_folder=False)
class TriangleRuleV2(BaseTriangle): def __init__(self, ss, s57, cs, use_valsous, use_contours=False, detect_deeps=False, multiplier=1.0, sounding_unit=sounding_units['feet'], progress=None): super(TriangleRuleV2, self).__init__(ss=ss, s57=s57, cs=cs, sounding_unit=sounding_unit, progress=progress) self.type = triangle_algos["TRIANGLE_RULE_v2"] self.use_valsous = use_valsous self.use_contours = use_contours self.detect_deeps = detect_deeps self.multiplier = multiplier self.gd = Geodesy() self.all_ss = self.ss.rec10s if self.s57 is None: self.all_s57 = list() else: self.all_s57 = self.s57.rec10s if self.cs is None: self.all_cs = list() else: self.all_cs = self.cs.rec10s self.points2d = None self.points3d = None self.delaunay = None self.edges_a = None self.edges_b = None self.triangles = None self.bad_triangles = None self.good_triangles = None def _append_flagged(self, x, y, note): """Helper function that append the note (if the feature position was already flagged) or add a new one""" # check if the point was already flagged for i in range(len(self.flagged_features[0])): if (self.flagged_features[0][i] == x) and (self.flagged_features[1][i] == y): self.flagged_features[2][i] = "%s, %s" % ( self.flagged_features[2][i], note) return # if not flagged, just append the new flagged position self.flagged_features[0].append(x) self.flagged_features[1].append(y) self.flagged_features[2].append(note) def run(self): """Execute the set of check of the feature scan algorithm""" logger.debug("using VALSOU features: %s" % self.use_valsous) logger.debug("using CONTOURs: %s" % self.use_contours) logger.debug("using deeps detection: %s" % self.detect_deeps) logger.debug("using chart sounding unit: %s" % self.csu) if self.csu == sounding_units["meters"]: logger.debug("using multiplier: %s" % self.multiplier) self._collect_points() self._triangulate() self._flag() self._plot() def _collect_points(self): logger.debug('collecting points to triangulate ...') self.progress.add(quantum=10, text="Collecting points to triangulate") xs = list() ys = list() zs = list() for ft in self.all_s57: # just for soundings if len(ft.geo3s) > 0: for geo3 in ft.geo3s: xs.append(geo3.x) ys.append(geo3.y) zs.append(geo3.z) elif ft.acronym == 'DEPCNT': if not self.use_contours: continue valdco = 0 for attr in ft.attributes: if attr.acronym == "VALDCO": try: valdco = float(attr.value) except ValueError: pass break # logger.debug('%s -> %.3f m (%d points)' % (ft.acronym, valdco, len(ft.geo2s))) for idx, geo2 in enumerate(ft.geo2s): if idx % 20: continue xs.append(geo2.x) ys.append(geo2.y) zs.append(valdco) # for all the other no-sounding features else: for attr in ft.attributes: if (len(ft.geo2s)) == 1: if (attr.acronym == "VALSOU") and self.use_valsous: try: z = float(attr.value) xs.append(ft.geo2s[0].x) ys.append(ft.geo2s[0].y) zs.append(z) # print('added: %s' % z) except ValueError: pass break self.points2d = np.column_stack((xs, ys)) self.points3d = np.column_stack((xs, ys, zs)) logger.debug('collected points: %d' % len(xs)) def _triangulate(self): logger.debug('triangulating') self.progress.add(quantum=10, text="Triangulating") self.delaunay = Delaunay(self.points2d) # print(self.delaunay.simplices) # calculate the length of all the edges len_edges = list() # noinspection PyUnresolvedReferences for tri in self.points3d[self.delaunay.simplices]: # print(tri[0][0], tri[0][1], tri[0][0], tri[0][1]) len_a = self.gd.distance(long_1=tri[0][0], lat_1=tri[0][1], long_2=tri[1][0], lat_2=tri[1][1]) len_edges.append(len_a) len_b = self.gd.distance(long_1=tri[1][0], lat_1=tri[1][1], long_2=tri[2][0], lat_2=tri[2][1]) len_edges.append(len_b) len_c = self.gd.distance(long_1=tri[2][0], lat_1=tri[2][1], long_2=tri[0][0], lat_2=tri[0][1]) len_edges.append(len_c) # print(len_edges) # print(np.mean(len_edges), np.median(len_edges), np.std(len_edges)) th_len = float(np.mean(len_edges) + 2 * np.std(len_edges)) logger.debug('removing threshold: %.1f m' % th_len) # remove the triangles with too long edges self.good_triangles = list() self.bad_triangles = list() # noinspection PyUnresolvedReferences for idx, tri in enumerate(self.points3d[self.delaunay.simplices]): if (len_edges[idx * 3 + 0] > th_len) \ or (len_edges[idx * 3 + 1] > th_len) \ or (len_edges[idx * 3 + 2] > th_len): self.bad_triangles.append(idx) # logger.debug('removing triangle #%03d: %s/%s/%s' # % (idx, len_edges[idx*3 + 0], len_edges[idx*3 + 1], len_edges[idx*3 + 2])) continue else: self.good_triangles.append(idx) # noinspection PyUnresolvedReferences self.triangles = np.delete(self.delaunay.simplices, self.bad_triangles, axis=0) # noinspection PyUnresolvedReferences self.rem_triangles = np.delete(self.delaunay.simplices, self.good_triangles, axis=0) # prepare edge for TIN output self.edges_a = [[], []] self.edges_b = [[], []] for tri in self.points3d[self.triangles]: self.edges_a[0].append(tri[0][0]) self.edges_a[1].append(tri[0][1]) self.edges_b[0].append(tri[1][0]) self.edges_b[1].append(tri[1][1]) self.edges_a[0].append(tri[1][0]) self.edges_a[1].append(tri[1][1]) self.edges_b[0].append(tri[2][0]) self.edges_b[1].append(tri[2][1]) self.edges_a[0].append(tri[2][0]) self.edges_a[1].append(tri[2][1]) self.edges_b[0].append(tri[0][0]) self.edges_b[1].append(tri[0][1]) # print("- a: %s, %s" % (tri[0][0], tri[0][1])) # print("- b: %s, %s" % (tri[1][0], tri[1][1])) # print("- c: %s, %s\n" % (tri[2][0], tri[2][1])) def _flag(self): logger.debug('searching SS to flag') self.progress.add(quantum=10, text="Searching SS to flag") for ft in self.all_ss: # skip if the feature has not exactly 1 point if len(ft.geo3s) != 1: continue p = np.array([ (ft.geo3s[0].x, ft.geo3s[0].y), ]) ret = self.delaunay.find_simplex(p) # skip checks if ret == -1: # out of all the triangles continue if ret in self.bad_triangles: # within a bad triangle continue # flagging using different criteria if self.csu == sounding_units['feet']: tri = self.points3d[self.delaunay.simplices[ret]] min_z = np.min(tri[0, :, 2]) ft_z = ft.geo3s[0].z ft_z_feet = round(ft_z * 3.28084) min_z_feet = round(min_z * 3.28084) diff_z_feet = min_z_feet - ft_z_feet # print(tri) # print(ft_z, ft_z_feet, min_z, min_z_feet, diff_z_feet) if diff_z_feet > 0.01: self._append_flagged(ft.geo3s[0].x, ft.geo3s[0].y, "%.3f" % diff_z_feet) continue if self.detect_deeps: max_z = np.max(tri[0, :, 2]) max_z_feet = round(max_z * 3.28084) diff_z_feet = max_z_feet - ft_z_feet if diff_z_feet < -3.28084: self._append_flagged(ft.geo3s[0].x, ft.geo3s[0].y, "%.3f" % diff_z_feet) continue elif self.csu == sounding_units['fathoms']: tri = self.points3d[self.delaunay.simplices[ret]] ft_z = ft.geo3s[0].z min_z = np.min(tri[0, :, 2]) max_z = np.max(tri[0, :, 2]) # this rule is coming from the Nautical Charting Manual (11 fathom danger curve) if (ft_z < 20.1168) and ( min_z < 20.1168): # 11 fathoms = 20.1168 meters ft_z_feet = round(ft_z * 3.28084) min_z_feet = round(min_z * 3.28084) diff_z_feet = min_z_feet - ft_z_feet # we also need fathoms for the output differences ft_z_fathoms = round(ft_z * 0.546807) min_z_fathoms = round(min_z * 0.546807) diff_z_fathoms = min_z_fathoms - ft_z_fathoms if diff_z_feet > 0.01: self._append_flagged(ft.geo3s[0].x, ft.geo3s[0].y, "%.3f" % diff_z_fathoms) continue if self.detect_deeps: # max_z_feet = round(max_z * 3.28084) # diff_z_feet = max_z_feet - ft_z_feet max_z_fathoms = round(max_z * 0.546807) diff_z_fathoms = max_z_fathoms - ft_z_fathoms if diff_z_fathoms < -0.546807: self._append_flagged(ft.geo3s[0].x, ft.geo3s[0].y, "%.3f" % diff_z_fathoms) # print(tri) # print(ft_z, ft_z_feet, min_z, min_z_feet, diff_z_feet) # print(ft_z, ft_z_fathoms, min_z, min_z_fathoms, diff_z_fathoms) continue else: ft_z_fathoms = round(ft_z * 0.546807) min_z_fathoms = round(min_z * 0.546807) diff_z_fathoms = min_z_fathoms - ft_z_fathoms if diff_z_fathoms > 0.01: self._append_flagged(ft.geo3s[0].x, ft.geo3s[0].y, "%.3f" % diff_z_fathoms) continue if self.detect_deeps: max_z_fathoms = round(max_z * 0.546807) diff_z_fathoms = max_z_fathoms - ft_z_fathoms if diff_z_fathoms < -0.546807: self._append_flagged(ft.geo3s[0].x, ft.geo3s[0].y, "%.3f" % diff_z_fathoms) # print(tri) # print(ft_z, ft_z_fathoms, min_z, min_z_fathoms, diff_z_fathoms) continue elif self.csu == sounding_units['meters']: tri = self.points3d[self.delaunay.simplices[ret]] min_z = np.min(tri[0, :, 2]) ft_z = ft.geo3s[0].z diff_z = min_z - ft_z # print(tri) # print(ft_z, ft_z_feet, min_z, min_z_feet, diff_z_feet) if diff_z > self.multiplier: self._append_flagged(ft.geo3s[0].x, ft.geo3s[0].y, "%.3f" % diff_z) continue if self.detect_deeps: max_z = np.max(tri[0, :, 2]) diff_z = max_z - ft_z if diff_z < -self.multiplier: self._append_flagged(ft.geo3s[0].x, ft.geo3s[0].y, "%.3f" % diff_z) continue else: raise RuntimeError("unknown criteria: %s" % self.csu) def _plot(self, save_fig=False): logger.debug('plotting') self.progress.add(quantum=10, text="Plotting") if not save_fig: plt.ion() ticks = 0.1 def latitudes(y, pos): """The two args are the value and tick position""" if ticks > 0.9: ret = '%.0f' % y elif ticks > 0.09: ret = '%.1f' % y elif ticks > 0.009: ret = '%.2f' % y else: ret = '%.3f' % y return ret def longitudes(x, pos): """The two args are the value and tick position""" if ticks > 0.9: ret = '%.0f' % x elif ticks > 0.09: ret = '%.1f' % x elif ticks > 0.009: ret = '%.2f' % x else: ret = '%.3f' % x return ret lat_formatter = FuncFormatter(latitudes) lon_formatter = FuncFormatter(longitudes) fig = plt.figure(1, figsize=(10, 10)) ax = fig.add_subplot(111) ax.patch.set_facecolor('0.35') ax.triplot(self.points3d[:, 0], self.points3d[:, 1], self.rem_triangles, zorder=1, color='0.5', lw=0.4, linestyle='--') ax.triplot(self.points3d[:, 0], self.points3d[:, 1], self.triangles, zorder=2, color='0.55', lw=0.8) # noinspection PyUnresolvedReferences ax.scatter(self.points3d[:, 0], self.points3d[:, 1], c=self.points3d[:, 2], zorder=3, marker='o', s=14, lw=0, cmap=plt.cm.GnBu) # noinspection PyUnresolvedReferences m1 = plt.cm.ScalarMappable(cmap=plt.cm.GnBu) m1.set_array(self.points3d[:, 2]) cb1 = plt.colorbar(m1) cb1.set_label('depth [m]', size=9) # noinspection PyUnresolvedReferences diff_cmap = plt.cm.autumn_r if self.detect_deeps: # noinspection PyUnresolvedReferences diff_cmap = plt.cm.RdYlGn_r # noinspection PyUnresolvedReferences zs = [float(x) for x in self.flagged_features[2]] # noinspection PyUnresolvedReferences ax.scatter(self.flagged_features[0], self.flagged_features[1], c=zs, zorder=4, marker='x', s=10, lw=1, cmap=diff_cmap) # noinspection PyUnresolvedReferences m2 = plt.cm.ScalarMappable(cmap=diff_cmap) m2.set_array(zs) cb2 = plt.colorbar(m2) if self.csu == sounding_units['feet']: cb2.set_label('depth difference [feet]', size=9) elif self.csu == sounding_units['meters']: cb2.set_label('depth difference [m]', size=9) else: cb2.set_label('depth difference [fathoms]', size=9) ax.set_aspect('equal') ax.grid(True, color='0.45') ticks = min((ax.get_yticks()[1] - ax.get_yticks()[0]), (ax.get_xticks()[1] - ax.get_xticks()[0])) ax.yaxis.set_major_formatter(lat_formatter) ax.xaxis.set_major_formatter(lon_formatter) plt.show()