def getMarkData(
        self,
        code,
        index=None,
        fillDays=False,
        after=None,
        before=None,
        increment=1.0,
        xyz0=None,
        xyz0Date=None,
    ):
        """
        Get the time series for a geodetic mark.  Retrieves the official coordinates, calculates the
        deformation model at the required dates, and constructs a timeseries from them.

        Can supply a list of dates as an index, a from date, to date, and increment, 
        or use the default dates.

        The reference coordinate can be explicitly supplied, or calculated for a reference date. 
        Otherwise the NZGD2000 XYZ value will be used.
        """

        if self._deformationModelDirectory is None:
            raise RuntimeError(
                "GDB timeseries deformation moel transformation is not defined"
            )
        if self._itrfTransformation is None:
            raise RuntimeError("GDB timeseries ITRF transformation is not defined")

        markdata = GDB.get(code)
        if markdata is None:
            raise RuntimeError(
                "GDB timeseries for " + code + " not available - mark not in GDB"
            )
        coord = markdata.coordinate
        lon, lat, hgt = coord.longitude, coord.latitude, coord.height
        markxyz = GRS80.xyz(lon, lat, hgt)
        function = lambda d: GRS80.xyz(self._itrfTransformation(lon, lat, hgt, d))
        if xyz0 is None and xyz0Date is not None:
            xyz0 = function(xyz0Date)
        if xyz0 is None:
            xyz0 = markxyz
        gdbts = CORS_Timeseries.FunctionTimeseries(
            function,
            code=code,
            solutiontype="gdb",
            index=index,
            fillDays=fillDays,
            after=after,
            before=before,
            xyz0=xyz0,
        )
        return GDB_Timeseries_Calculator.StationData(code, markxyz, markdata, gdbts)
Ejemplo n.º 2
0
    def __init__(
        self,
        code,
        lon=None,
        lat=None,
        hgt=None,
        robg="B10",
        cosy="NZGD2000",
        rdate="2000.01.01",
        comment="",
    ):
        self.code = code
        markdata = GDB.get(code)
        if not markdata:
            raise RuntimeError(code + " is not in the geodetic database")
        gdbcoord = [coord.longitude, coord.latitude, coord.height]
        if lon is None or lat is None or hgt is None:
            lon, lat, hgt = gdbcoord

        self.lon = lon
        self.lat = lat
        self.hgt = hgt
        self.robg = robg
        self.cosy = cosy
        self.rdate = rdate
        self.comment = comment
        self.gdbCoord = gdbcoord
        self.xyz0 = GRS80.xyz(self.gdbCoord)
        self.enu_axes = GRS80.enu_axes(self.gdbCoord[0], self.gdbCoord[1])
        pass
Ejemplo n.º 3
0
def main():

    epilog = """
    Available coordinate systems are NZGD2000, ITRFxxxx.  These are geographic coordinates.
    For geocentric coordinates add _XYZ.  Multiple coordinate systems can be entered, eg
    ITRF2008+NZGD2000

    The epoch can be formatted as YYYY-MM-DD or now-#### for #### days before now.
    A range of dates can be selected as date1:date2
    Appended to this can be criteria :W# to select a day of the week (0-6, 0=Monday) or
    :M# for the day of month.
    

    The output CSV file can contain strings {yyyy} {mm} and {dd} that are replaced 
    the year, month and day.
    """
    parser = argparse.ArgumentParser(
        description="Export cors station coordinates from time series database",
        epilog=epilog,
        parents=[deformationModelArguments()],
    )
    parser.add_argument("timeseries_data",
                        help="Data source for CORS timeseries coordinates")
    parser.add_argument(
        "epoch", help="Epoch or range at which coordinates are required")
    parser.add_argument("output_csv_file", help="File to write coordinates to")
    parser.add_argument(
        "-c",
        "--coordinate-systems",
        help="Coordinate systems to output (default NZGD2000)",
    )
    parser.add_argument(
        "-i",
        "--cors-itrf",
        default="ITRF2008",
        help="CORS timeseries ITRF reference frame",
    )
    parser.add_argument(
        "-s",
        "--stations",
        help="Specifies stations to export, separated by +, or @filename",
    )
    parser.add_argument(
        "-n",
        "--nz-only",
        action="store_true",
        help="Only list New Zealand stations (including Chathams)",
    )
    parser.add_argument(
        "-t",
        "--solution-type",
        help=
        "Solution type to extract (required if data includes more than one type)",
    )

    args = parser.parse_args()

    cors_itrf = args.cors_itrf
    tslist = None
    outputfiles = {}
    try:
        tslist = TimeseriesList(args.timeseries_data,
                                solutiontype=args.solution_type,
                                normalize=True)

        try:
            parts = args.epoch.split(":")
            if len(parts) < 2:
                parts.append(parts[0])
            startenddate = []
            for p in parts[:2]:
                match = re.match(r"^now-(\d+)$", p, re.I)
                if match:
                    dtp = dt.date.today() - dt.timedelta(
                        days=int(match.group(1)))
                    startenddate.append(
                        dt.datetime.combine(dtp, dt.time(0, 0, 0)))
                else:
                    startenddate.append(
                        dt.datetime.strptime(args.epoch, "%Y-%m-%d"))
            useday = lambda d: True
            if len(parts) > 2:
                match = re.match(r"^([MW])(\d\d?)$", parts[2].upper())
                if not match:
                    raise RuntimeError("Invalid epoch date selector " +
                                       parts[2])
                dayno = int(match.group(2))
                if match.group(1) == "M":
                    useday = lambda d: d.day == dayno
                else:
                    useday = lambda d: d.weekday() == dayno
            startdate, enddate = startenddate
            increment = dt.timedelta(days=1)
            while startdate <= enddate:
                calcdate = startdate
                startdate = startdate + increment
                if not useday(calcdate):
                    continue
                filename = args.output_csv_file
                filename = filename.replace("{yyyy}",
                                            "{0:04d}".format(calcdate.year))
                filename = filename.replace("{mm}",
                                            "{0:02d}".format(calcdate.month))
                filename = filename.replace("{dd}",
                                            "{0:02d}".format(calcdate.day))
                filename = filename.replace(
                    "{ddd}", "{0:03d}".format(calcdate.timetuple().tm_yday))
                if filename not in outputfiles:
                    outputfiles[filename] = []
                outputfiles[filename].append(calcdate)
        except:
            raise RuntimeError(
                "Invalid calculation epoch - must be YYYY-MM-DD:" + args.epoch)
        if len(outputfiles) == 0:
            raise RuntimeError("No dates defined for station coordinates")

        itrf_nzgd2000 = None
        nzgd2000_version = ""
        conversions = []
        geodetic_suffix = "_lon _lat _ehgt".split()
        xyz_suffix = "_X _Y _Z".split()
        coord_cols = []

        for cs in (args.coordinate_systems or "NZGD2000").upper().split("+"):
            match = re.match(r"^(NZGD2000|ITRF(?:19|20)\d\d)(_XYZ)?$", cs)
            if not match:
                print("Invalid coordinate system requested:", cs)
                sys.exit()
            csbase = match.group(1)
            isgeodetic = match.group(2) != "_XYZ"
            transformation = None
            if csbase == "NZGD2000":
                if not itrf_nzgd2000:
                    defmodel = loadDeformationModel(args)
                    nzgd2000_version = defmodel.version()
                    itrf_nzgd2000 = ITRF_NZGD2000.Transformation(
                        itrf=cors_itrf, model=defmodel)

                def transformation(xyz, date):
                    llh = GRS80.geodetic(xyz)
                    llh2k = itrf_nzgd2000.transform(llh[0], llh[1], llh[2],
                                                    date)
                    return GRS80.xyz(llh2k)

            else:
                transformation = ITRF.Transformation(from_itrf=cors_itrf,
                                                     to_itrf=csbase).transform
            conversions.append((transformation, isgeodetic))
            coord_cols.extend((csbase + suffix for suffix in (
                geodetic_suffix if isgeodetic else xyz_suffix)))

        check_code = lambda code: True
        if args.stations:
            stations = []
            for s in args.stations.split("+"):
                if s.startswith("@"):
                    try:
                        with open(s[1:]) as sf:
                            stations.extend(sf.read().upper().split())
                    except:
                        raise RuntimeError("Cannot open station list file " +
                                           s[1:])
                elif s != "":
                    stations.append(s.upper())
            if len(stations) == 0:
                raise RuntimeError("No stations specifed in " + args.stations)
            check_code = lambda code: code.upper() in stations

        check_xyz = lambda xyz: True
        if args.nz_only:
            nzxyz = GRS80.xyz(177.0, -42.0)
            nzdist = 1000000.0
            check_xyz = lambda xyz: (math.sqrt(
                (xyz[0] - nzxyz[0])**2 + (xyz[1] - nzxyz[1])**2 +
                (xyz[2] - nzxyz[2])**2) < nzdist)

        for output_csv_file in sorted(outputfiles):
            calcdates = outputfiles[output_csv_file]
            ncoord = 0
            buildfile = output_csv_file + ".temp"
            with open(buildfile, "w") as csvf:
                writer = csv.writer(csvf)
                header = "code epoch".split()
                if nzgd2000_version:
                    header.append("nzgd2000_version")
                header.extend(coord_cols)
                writer.writerow(header)
                for code in tslist.codes():
                    if not check_code(code):
                        continue
                    for calcdate in calcdates:
                        ts = tslist.get(code).getData(enu=False)
                        try:
                            crd = ts.ix[calcdate]
                        except KeyError:
                            continue

                        if type(crd) is pd.DataFrame:
                            print(
                                "Ambiguous coordinates {0} at date {1}".format(
                                    code, calcdate))
                            crd = crd[-1:].ix[calcdate]
                        xyz_2008 = [crd.x, crd.y, crd.z]
                        if not check_xyz(xyz_2008):
                            continue

                        row = [code, calcdate.strftime("%Y-%m-%d")]
                        if nzgd2000_version:
                            row.append(nzgd2000_version)
                        for transformation, isgeodetic in conversions:
                            try:
                                xyz = transformation(xyz_2008, calcdate)
                                if isgeodetic:
                                    llh = GRS80.geodetic(xyz)
                                    row.extend([
                                        "{0:.9f}".format(llh[0]),
                                        "{0:.9f}".format(llh[1]),
                                        "{0:.4f}".format(llh[2]),
                                    ])
                                else:
                                    row.extend([
                                        "{0:.4f}".format(xyz[0]),
                                        "{0:.4f}".format(xyz[1]),
                                        "{0:.4f}".format(xyz[2]),
                                    ])
                            except OutOfRangeError:
                                row.extend(["", "", ""])
                        writer.writerow(row)
                        ncoord += 1
            if ncoord == 0:
                os.unlink(buildfile)
                print(output_csv_file +
                      " not built as coordinates not available")
            else:
                os.rename(buildfile, output_csv_file)

    except RuntimeError as e:
        print(e)
Ejemplo n.º 4
0
 def transformation(xyz, date):
     llh = GRS80.geodetic(xyz)
     llh2k = itrf_nzgd2000.transform(llh[0], llh[1], llh[2],
                                     date)
     return GRS80.xyz(llh2k)
Ejemplo n.º 5
0
def main():
    parser = argparse.ArgumentParser(description="""
    Calculate the corrections to convert an ITRF coordinate to or from 
    an NZGD2000 coordinate on a longitude/latitude grid.
    The corrections are generated in a comma separated (CSV) file.""")
    parser.add_argument("min_lon", type=float, help="Minimum longitude")
    parser.add_argument("min_lat", type=float, help="Minimum latitude")
    parser.add_argument("max_lon", type=float, help="Maximum longitude")
    parser.add_argument("max_lat", type=float, help="Maximum latitude")
    parser.add_argument("nlon", type=float, help="Number of longitude values")
    parser.add_argument("nlat", type=float, help="Number of latitude values")
    parser.add_argument("output_file", type=str, help="Output file name")
    parser.add_argument(
        "-d",
        "--date",
        type=str,
        default="now",
        help="Date of transformation (default current date)",
    )
    parser.add_argument(
        "-i",
        "--itrf",
        type=str,
        default="ITRF2008",
        help="ITRF reference frame version (default ITRF2008)",
    )
    parser.add_argument(
        "-m",
        "--model-dir",
        type=str,
        default="../model",
        help="Deformation model base directory (default ../model)",
    )
    parser.add_argument(
        "-v",
        "--version",
        type=str,
        help="Version of deformation model to use (default current version)",
    )
    parser.add_argument(
        "-t",
        "--type",
        choices=("llh", "enu", "xyz"),
        default="llh",
        help="Type of correction to calculate",
    )
    parser.add_argument(
        "-s",
        "--size",
        action="store_true",
        help=
        "Use grid cell size (dlon,dlat) instead of number of values (nlon,nlat)",
    )
    parser.add_argument(
        "-r",
        "--reverse",
        action="store_true",
        help="Generate grid to convert NZGD2000 to ITRF",
    )
    parser.add_argument(
        "-o",
        "--order",
        choices="es en ws wn se sw ne nw".split(),
        default="es",
        help="Grid order (start corner and first axis to increment)",
    )
    parser.add_argument("-q",
                        "--quiet",
                        action="store_true",
                        help="Suppress output")
    parser.add_argument(
        "--cache",
        choices=("ignore", "clear", "reset", "use"),
        default="use",
        help="Deformation model cache option (requires pytables)",
    )
    parser.add_argument("--logging",
                        action="store_true",
                        help="Enable trace logging")

    args = parser.parse_args()

    modeldir = args.model_dir
    version = args.version
    date = args.date
    try:
        date = Time.Parse(args.date)
    except:
        print("Invalid date " + v + " requested, must be formatted YYYY-MM-DD")
        sys.exit()
    outputfile = args.output_file
    itrf = args.itrf
    increment = args.size
    order = args.order
    reverse = args.reverse
    corrtype = args.type
    quiet = args.quiet
    usecache = args.cache in ("use", "reset")
    clearcache = args.cache in ("clear", "reset")

    if args.logging:
        logging.basicConfig(level=logging.INFO)

    if not modeldir:
        modeldir = os.environ.get("NZGD2000_DEF_MODEL")

    if not modeldir:
        from os.path import dirname, abspath, join, isdir, exists

        modeldir = join(dirname(dirname(abspath(sys.argv[0]))), "model")
        modelcsv = join(modeldir, "model.csv")
        if not isdir(modeldir) or not exists(modelcsv):
            modeldir = "model"

    # Use a loop to make exiting easy...

    for loop in [1]:
        # Setup the transformation
        transform = None
        try:
            transform = ITRF_NZGD2000.Transformation(
                itrf,
                toNZGD2000=not args.reverse,
                modeldir=modeldir,
                version=version,
                usecache=usecache,
                clearcache=clearcache,
            )
        except ModelDefinitionError:
            print("Error loading model:")
            print(str(sys.exc_info()[1]))
            break
        except RuntimeError:
            print(str(sys.exc_info()[1]))
            break

        # Determine the source for input

        coords = []
        try:
            min_lon = args.min_lon
            min_lat = args.min_lat
            max_lon = args.max_lon
            max_lat = args.max_lat
            if max_lon <= min_lon or max_lat <= min_lat:
                raise ValueError(
                    "Minimum latitude or longitude larger than maximum")
            lonval = None
            latval = None
            if increment:
                dlon = args.nlon
                dlat = args.nlat
                if dlon <= 0 or dlat <= 0:
                    raise ValueError("")
                lonval = np.arange(min_lon, max_lon + dlon * 0.99, dlon)
                latval = np.arange(min_lat, max_lat + dlat * 0.99, dlat)
            else:
                nlon = int(args.nlon)
                nlat = int(args.nlat)
                if nlon < 2 or nlat < 2:
                    raise ValueError(
                        "Must be at least two longitude and latitude values")
                lonval = np.linspace(min_lon, max_lon, nlon)
                latval = np.linspace(min_lat, max_lat, nlat)

            if "w" in order:
                lonval = lonval[::-1]
            if "n" in order:
                latval = latval[::-1]
            if order[0] in "ew":
                for lat in latval:
                    for lon in lonval:
                        coords.append((lon, lat))
            else:
                for lon in lonval:
                    for lat in latval:
                        coords.append((lon, lat))

        except ValueError as e:
            print("Invalid grid definition: " + str(e))
            break

        # Create the output file

        if not quiet:
            if reverse:
                print("Calculating NZGD2000 to " + itrf + " corrections at " +
                      str(date))
            else:
                print("Calculating " + itrf + " to NZGD2000 corrections at " +
                      str(date))
            print("Deformation model " + transform.model.name() + " version " +
                  transform.version)

        try:
            outstream = open(outputfile, "w")
        except:
            print("Cannot open output file", outputfile)
            break

        if corrtype == "llh":
            outstream.write("lon,lat,dlon,dlat,dhgt\n")
        elif corrtype == "enu":
            outstream.write("lon,lat,de,dn,dh\n")
        elif corrtype == "xyz":
            outstream.write("lon,lat,dx,dy,dz\n")

        ncalc = 0
        nrngerr = 0
        nmissing = 0
        hgt = 0.0
        rvs = -1.0 if reverse else 1.0
        for lon, lat in coords:
            try:
                llh = transform(lon, lat, hgt, date)
                if corrtype == "llh":
                    outstream.write(
                        "{0:.5f},{1:.5f},{2:.9f},{3:.9f},{4:.4f}\n".format(
                            lon,
                            lat,
                            rvs * (llh[0] - lon),
                            rvs * (llh[1] - lat),
                            rvs * llh[2],
                        ))
                elif corrtype == "enu":
                    dedln, dndlt = GRS80.metres_per_degree(lon, lat)
                    outstream.write(
                        "{0:.5f},{1:.5f},{2:.4f},{3:.4f},{4:.4f}\n".format(
                            lon,
                            lat,
                            rvs * dedln * (llh[0] - lon),
                            rvs * dndlt * (llh[1] - lat),
                            rvs * llh[2],
                        ))
                elif corrtype == "xyz":
                    xyz0 = GRS80.xyz(lon, lat, hgt)
                    xyz1 = GRS80.xyz(llh[0], llh[1], llh[2])
                    outstream.write(
                        "{0:.5f},{1:.5f},{2:.4f},{3:.4f},{4:.4f}\n".format(
                            lon,
                            lat,
                            rvs * (xyz1[0] - xyz0[0]),
                            rvs * (xyz1[1] - xyz0[1]),
                            rvs * (xyz1[2] - xyz0[2]),
                        ))
                ncalc += 1
            except OutOfRangeError:
                nrngerr += 1
            except UndefinedValueError:
                nmissing += 1
            except:
                raise
                print(str(sys.exc_info()[1]))
                nerror += 1

        outstream.close()

        if not quiet:
            print("{0} corrections calculated".format(ncalc))
        if nrngerr > 0:
            print(
                "{0} points were outside the valid range of the model".format(
                    nrngerr))
        if nmissing > 0:
            print("{0} deformation values were undefined in the model".format(
                nmissing))
Ejemplo n.º 6
0
        def fixStation(self, xyz, exclude=None):
            if self.xyz is None or True:
                self.locator.write("Fixing station {0}\n".format(self.code))
            self.setXyz(xyz)
            # Fix any HA observation sets that can be defined
            hafixstations = set()

            # Don't calculate orientations at excluded stations
            exclude = exclude or {}
            for tostn in self.obs:
                if tostn is None or tostn.xyz is None:
                    continue
                obs = self.obs[tostn]
                if "HA" in obs:
                    for haobs in obs["HA"]:
                        if haobs.obsset.defined:
                            continue
                        if haobs.obsset.instStation in exclude:
                            continue
                        stnfrom = self.station()
                        stnto = tostn.station()
                        if haobs.reverse:
                            stnfrom, stnto = stnto, stnfrom
                        az = stnfrom.azimuthTo(stnto)
                        fromcode = stnfrom.code()
                        self.locator.write(
                            "Fixing orientation of HA observations {1} at {0}\n"
                            .format(fromcode, haobs.obsset.id))
                        referredcodes = [
                            stn.code for stn in haobs.obsset.stations
                            if stn.code != fromcode
                        ]
                        self.locator.write(
                            "   Provides azimuths to {0}\n".format(
                                ", ".join(referredcodes)))

                        haobs.obsset.setOrientation(az - haobs.obsvalue.value)
                        if haobs.reverse:
                            hafixstations.add(tostn)

            # Now work out coordinates can be calculated

            # Don't calculate trial coordinates based on joins between
            # excluded sets.
            exclude = exclude or {}
            if self not in exclude:
                exclude = {}

            for tostn in self.obs:
                if tostn is None or tostn.xyz is not None:
                    continue
                # self.locator.write("Attempting to coordinate {0}\n".format(tostn.code))
                obs = self.obs[tostn]
                if "SD" not in obs and "HD" not in obs:
                    self.locator.write(
                        "   Cannot fix {0} - no distance\n".format(tostn.code))
                    continue

                # Work out an azimuth
                azimuths = []
                if "AZ" in obs:
                    azimuths = [
                        o.obsvalue.value + (180.0 if o.reverse else 0)
                        for o in obs["AZ"]
                    ]
                if "HA" in obs:
                    for o in obs["HA"]:
                        if not o.obsset.defined:
                            continue
                        az = o.obsvalue.value + o.obsset.orientation
                        if o.reverse:
                            az += 180.0
                        azimuths.append(az)
                # If no horizontal angles defined yet, then can't set trial coord yet
                if len(azimuths) == 0:
                    self.locator.write(
                        "   Cannot fix {0} - no azimuth\n".format(tostn.code))
                    continue
                azimuths = np.array(azimuths)
                azimuths *= math.radians(1.0)
                az0 = azimuths[0]
                azimuths = (np.remainder(
                    np.array(azimuths) - az0 + math.pi, 2 * math.pi) + az0 -
                            math.pi)
                azimuth = np.mean(azimuths)

                llh0 = GRS80.geodetic(self.xyz)
                hgtdiff = 0.0
                if "LV" in obs:
                    hgtdiff = np.mean([o.obsvalue.value for o in obs["LV"]])
                # Only use ZD if don't have levelled height difference
                elif "ZD" in obs:
                    slope = True
                    distance = 0.0
                    if "SD" in obs:
                        distance = np.mean(
                            [o.obsvalue.value for o in obs["SD"]])
                        slope = True
                    else:
                        distance = np.mean(
                            [o.obsvalue.value for o in obs["HD"]])
                        slope = False

                    hgtdiffs = []
                    for o in obs["ZD"]:
                        angle = math.radians(o.obsvalue.value)
                        sd = math.sin(angle) * distance if slope else distance
                        corr = sd / (2.0 * (llh0[2] + MeanEarthRadius))
                        angle -= corr
                        hd = math.cos(angle) * distance
                        hd += o.obsvalue.insthgt
                        hd -= o.obsvalue.trgthgt
                        if o.reverse:
                            hd = -hd
                        hgtdiffs.append(hd)
                    hgtdiff = np.mean(hgtdiffs)
                if hgtdiff is None:
                    self.locator.write(
                        "   Cannot fix {0} - no height data\n".format(
                            tostn.code))
                    continue

                hordists = [o.obsvalue.value for o in obs.get("HD", [])]
                for o in obs.get("SD", []):
                    vdist = hgtdiff
                    if o.reverse:
                        vdist = -vdist
                    vdist += o.obsvalue.trgthgt - o.obsvalue.insthgt
                    hdist = o.obsvalue.value * o.obsvalue.value - vdist * vdist
                    if hdist > 0.0:
                        hordists.append(math.sqrt(hdist))
                    else:
                        hordists.append(0.0)
                hordist = np.mean(hordists)

                denu = np.array((hordist * math.sin(azimuth),
                                 hordist * math.cos(azimuth), hgtdiff))
                dxyz = self.enu_axes.T.dot(denu)
                txyz = self.xyz + dxyz
                llh1 = GRS80.geodetic(txyz)
                llh1[2] = llh0[2] + hgtdiff
                txyz = GRS80.xyz(llh1)
                tostn.addTrialXyz(self, txyz)
                self.locator.write(
                    "  Trial coordinate determined for {0}: {1}\n".format(
                        tostn.code, str(txyz)))

            # Finally refix any stations
            for stn in hafixstations:
                stn.fixStation(stn.xyz, exclude)
Ejemplo n.º 7
0
 def xyzOffset(self):
     xyz1 = GRS80.xyz(self.lon, self.lat, self.hgt)
     dxyz = xyz1 - self.xyz0
     return self.enu_axes.dot(dxyz)