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
Beispiel #2
0
    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)
Beispiel #6
0
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()