pycrs/parse.py
def from_proj4(proj4, strict=False):
    """
    Parse crs as proj4 formatted string or dict and return the resulting crs object.

    Arguments:

    - *proj4*: The proj4 representation as a string or dict.
    - *strict* (optional): When True, the parser is strict about names having to match
        exactly with upper and lowercases. Default is not strict (False).

    Returns:

    - A CS instance of the indicated type. 
    """
    # parse arguments into components
    # use args to create crs

    # TODO: SLIGTHLY MESSY STILL, CLEANUP..

    params = []

    if isinstance(proj4, dict):
        # add leading + sign as expected below, proj4 dicts do not have that
        partdict = dict([('+'+k,v) for k,v in proj4.items()])
    else: 
        partdict = dict([part.split("=") for part in proj4.split()
                         if len(part.split("=")) == 2 ])

    # INIT CODES
    # eg, +init=EPSG:1234
    if "+init" in partdict:

        # first, get the default proj4 string of the +init code
        codetype, code = partdict["+init"].split(":")
        if codetype == "EPSG":
            initproj4 = utils.crscode_to_string("epsg", code, "proj4")
        elif codetype == "ESRI":
            initproj4 = utils.crscode_to_string("esri", code, "proj4")

        # make the default into param dict
        initpartdict = dict([part.split("=") for part in initproj4.split()
                             if len(part.split("=")) == 2 ])

        # override the default with any custom params specified along with the +init code
        initpartdict.update(partdict)

        # rerun from_proj4() again on the derived proj4 params as if it was not made with the +init code
        del initpartdict["+init"]
        string = " ".join("%s=%s" % (key,val) for key,val in initpartdict.items())
        return from_proj4(string)

    # DATUM

    # datum param is required
    if "+datum" in partdict:
        
        # get predefined datum def
        datumname = partdict["+datum"]
        datumclass = datums.find(datumname, "proj4", strict)
        if datumclass:
            datum = datumclass()
        else:
            datum = datums.Unknown()

    else:
        datum = datums.Unknown()

    # ELLIPS

    # ellipse param is required
    ellips = None
    if "+ellps" in partdict:

        # get predefined ellips def
        ellipsname = partdict["+ellps"]
        ellipsclass = ellipsoids.find(ellipsname, "proj4", strict)
        if ellipsclass:
            ellips = ellipsclass()

    if not ellips:
        ellips = ellipsoids.Unknown()

    # TO WGS 84 COEFFS
    if "+towgs84" in partdict:
        coeffs = partdict["+towgs84"].split(",")
        datumshift = parameters.DatumShift(coeffs)

        # TODO: if no datum, use ellips + towgs84 params to create the correct datum
        # ...??

    # COMBINE DATUM AND ELLIPS

    ## create datum and ellips param objs

    # +ellps loads all the required ellipsoid parameters
    # here we set or overwrite the parameters manually
    if "+a" in partdict:
        # semimajor radius
        ellips.semimaj_ax = parameters.SemiMajorRadius(partdict["+a"])        

    if "+b" in partdict:
        # semiminor radius
        ellips.semimin_ax = parameters.SemiMinorRadius(partdict["+b"])

    if "+f" in partdict:
        # flattening
        ellips.flat = parameters.Flattening(partdict["+f"])

    if "+rf" in partdict:
        # inverse flattening
        ellips.inv_flat = parameters.InverseFlattening(partdict["+rf"])

    # check that ellipsoid is sufficiently defined
    if ellips.semimaj_ax and ellips.semimin_ax:
        # +a (semimajor radius) and +b (semiminor radius) is enough and can be used to calculate flattening
        # see https://en.wikipedia.org/wiki/Flattening
        pass
    elif ellips.semimaj_ax and ellips.inv_flat:
        # alternatively, it is okay with if +a (semimajor) and +f (flattening) are specified
        pass
    elif ellips.semimaj_ax and ellips.flat:
        # alternatively, semimajor and +rf is also acceptable (the reciprocal/inverse of +f)
        pass
    else:
        raise FormatError("The format string is missing the required +ellps element, or the alternative manual specification of the +a with +b or +f/+rf elements: \n\t %s" % partdict)
    
    if "+datum" in partdict:
        datum.ellips = ellips

    elif "+towgs84" in partdict:
        datum.ellips = ellips
        datum.datumshift = datumshift

    else:
        datum.ellips = ellips

    # PRIME MERIDIAN

    # set default
    prime_mer = parameters.PrimeMeridian(0)

    # overwrite with user input
    if "+pm" in partdict:
        prime_mer = parameters.PrimeMeridian(partdict["+pm"])

    # ANGULAR UNIT    

    ## proj4 cannot set angular unit, so just set to default
    angunit = units.Degree() 

    # GEOGCS (note, currently does not load axes)

    geogcs = containers.GeogCS("Unknown", datum, prime_mer, angunit) #, twin_ax)

    # PROJECTION
    
    if "+proj" in partdict:

        # get predefined proj def
        projname = partdict["+proj"]
        projclass = projections.find(projname, "proj4", strict)
        if projclass:
            proj = projclass()
        elif projname == "longlat":
            # proj4 special case, longlat as projection name means unprojected geogcs
            proj = None
        else:
            raise NotImplementedError("Unsupported projection: The specified projection name %r could not be found in the list of supported projections" % projname)

    else:
        raise FormatError("The format string is missing the required +proj element")

    if proj:

        # Because proj4 has no element hierarchy, using automatic element find() would
        # ...would not be very effective, as that would need a try-fail approach for each
        # ...element type (parameter, projection, datum, ellipsoid, unit).
        # ...Instead load each element individually.

        # CENTRAL MERIDIAN

        if "+lon_0" in partdict:
            val = partdict["+lon_0"]
            obj = parameters.CentralMeridian(val)
            params.append(obj)

        # FALSE EASTING

        if "+x_0" in partdict:
            val = partdict["+x_0"]
            obj = parameters.FalseEasting(val)
            params.append(obj)

        # FALSE NORTHING

        if "+y_0" in partdict:
            val = partdict["+y_0"]
            obj = parameters.FalseNorthing(val)
            params.append(obj)

        # SCALING FACTOR
        
        if "+k_0" in partdict or "+k" in partdict:
            if "+k_0" in partdict: val = partdict["+k_0"]
            elif "+k" in partdict: val = partdict["+k"]
            obj = parameters.ScalingFactor(val)
            params.append(obj)

        # LATITUDE ORIGIN

        if "+lat_0" in partdict:
            val = partdict["+lat_0"]
            obj = parameters.LatitudeOrigin(val)
            params.append(obj)

        # LATITUDE TRUE SCALE

        if "+lat_ts" in partdict:
            val = partdict["+lat_ts"]
            obj = parameters.LatitudeTrueScale(val)
            params.append(obj)

        # LONGITUDE CENTER

        if "+lonc" in partdict:
            val = partdict["+lonc"]
            obj = parameters.LongitudeCenter(val)
            params.append(obj)

        # AZIMUTH

        if "+alpha" in partdict:
            val = partdict["+alpha"]
            obj = parameters.Azimuth(val)
            params.append(obj)

        # STD PARALLEL 1

        if "+lat_1" in partdict:
            val = partdict["+lat_1"]
            obj = parameters.LatitudeFirstStndParallel(val)
            params.append(obj)
            
        # STD PARALLEL 2

        if "+lat_2" in partdict:
            val = partdict["+lat_2"]
            obj = parameters.LatitudeSecondStndParallel(val)
            params.append(obj)
            
        # SATELLITE HEIGHT
        if "+h" in partdict:
            val = partdict["+h"]
            obj = parameters.SatelliteHeight(val)
            params.append(obj)

        # TILT ANGLE
        if "+tilt" in partdict:
            val = partdict["+tilt"]
            obj = parameters.TiltAngle(val)
            params.append(obj)

        # UNIT

        # get values
        if "+units" in partdict:
            # unit name takes precedence over to_meter
            unitname = partdict["+units"]
            unitclass = units.find(unitname, "proj4", strict)
            if unitclass:
                unit = unitclass() # takes meter multiplier from name, ignoring any custom meter multiplier
            else:
                raise FormatError("The specified unit name %r does not appear to be a valid unit name" % unitname)
        elif "+to_meter" in partdict:
            # no unit name specified, only to_meter conversion factor
            unit = units.Unknown()
            unit.metermultiplier.value = partdict["+to_meter"]
        else:
            # if nothing specified, defaults to meter
            unit = units.Meter()

        # PROJCS

        projcs = containers.ProjCS("Unknown", geogcs, proj, params, unit)
        return projcs

    else:
        # means projdef was None, ie unprojected longlat geogcs
        return geogcs
def from_proj4(proj4, strict=False):
    """
    Parse crs as proj4 formatted string or dict and return the resulting crs object.

    Arguments:

    - *proj4*: The proj4 representation as a string or dict.
    - *strict* (optional): When True, the parser is strict about names having to match
        exactly with upper and lowercases. Default is not strict (False).

    Returns:

    - A CS instance of the indicated type. 
    """
    # parse arguments into components
    # use args to create crs

    # TODO: SLIGTHLY MESSY STILL, CLEANUP..

    params = []

    if isinstance(proj4, dict):
        # add leading + sign as expected below, proj4 dicts do not have that
        partdict = dict([('+'+k,v) for k,v in proj4.items()])
    else: 
        partdict = dict([part.split("=") for part in proj4.split()
                         if len(part.split("=")) == 2 ])

    # INIT CODES
    # eg, +init=EPSG:1234
    if "+init" in partdict:

        # first, get the default proj4 string of the +init code
        codetype, code = partdict["+init"].split(":")
        if codetype == "EPSG":
            initproj4 = utils.crscode_to_string("epsg", code, "proj4")
        elif codetype == "ESRI":
            initproj4 = utils.crscode_to_string("esri", code, "proj4")

        # make the default into param dict
        initpartdict = dict([part.split("=") for part in initproj4.split()
                             if len(part.split("=")) == 2 ])

        # override the default with any custom params specified along with the +init code
        initpartdict.update(partdict)

        # rerun from_proj4() again on the derived proj4 params as if it was not made with the +init code
        del initpartdict["+init"]
        string = " ".join("%s=%s" % (key,val) for key,val in initpartdict.items())
        return from_proj4(string)

    # DATUM

    # datum param is required
    if "+datum" in partdict:
        
        # get predefined datum def
        datumname = partdict["+datum"]
        datumclass = datums.find(datumname, "proj4", strict)
        if datumclass:
            datum = datumclass()
        else:
            datum = datums.Unknown()

    else:
        datum = datums.Unknown()

    # ELLIPS

    # ellipse param is required
    ellips = None
    if "+ellps" in partdict:

        # get predefined ellips def
        ellipsname = partdict["+ellps"]
        ellipsclass = ellipsoids.find(ellipsname, "proj4", strict)
        if ellipsclass:
            ellips = ellipsclass()

    if not ellips:
        ellips = ellipsoids.Unknown()

    # TO WGS 84 COEFFS
    if "+towgs84" in partdict:
        coeffs = partdict["+towgs84"].split(",")
        datumshift = parameters.DatumShift(coeffs)

        # TODO: if no datum, use ellips + towgs84 params to create the correct datum
        # ...??

    # COMBINE DATUM AND ELLIPS

    ## create datum and ellips param objs

    # +ellps loads all the required ellipsoid parameters
    # here we set or overwrite the parameters manually
    if "+a" in partdict:
        # semimajor radius
        ellips.semimaj_ax = parameters.SemiMajorRadius(partdict["+a"])        

    if "+b" in partdict:
        # semiminor radius
        ellips.semimin_ax = parameters.SemiMinorRadius(partdict["+b"])

    if "+f" in partdict:
        # flattening
        ellips.flat = parameters.Flattening(partdict["+f"])

    if "+rf" in partdict:
        # inverse flattening
        ellips.inv_flat = parameters.InverseFlattening(partdict["+rf"])

    # check that ellipsoid is sufficiently defined
    if ellips.semimaj_ax and ellips.semimin_ax:
        # +a (semimajor radius) and +b (semiminor radius) is enough and can be used to calculate flattening
        # see https://en.wikipedia.org/wiki/Flattening
        pass
    elif ellips.semimaj_ax and ellips.inv_flat:
        # alternatively, it is okay with if +a (semimajor) and +f (flattening) are specified
        pass
    elif ellips.semimaj_ax and ellips.flat:
        # alternatively, semimajor and +rf is also acceptable (the reciprocal/inverse of +f)
        pass
    else:
        raise FormatError("The format string is missing the required +ellps element, or the alternative manual specification of the +a with +b or +f/+rf elements: \n\t %s" % partdict)
    
    if "+datum" in partdict:
        datum.ellips = ellips

    elif "+towgs84" in partdict:
        datum.ellips = ellips
        datum.datumshift = datumshift

    else:
        datum.ellips = ellips

    # PRIME MERIDIAN

    # set default
    prime_mer = parameters.PrimeMeridian(0)

    # overwrite with user input
    if "+pm" in partdict:
        prime_mer = parameters.PrimeMeridian(partdict["+pm"])

    # ANGULAR UNIT    

    ## proj4 cannot set angular unit, so just set to default
    angunit = units.Degree() 

    # GEOGCS (note, currently does not load axes)

    geogcs = containers.GeogCS("Unknown", datum, prime_mer, angunit) #, twin_ax)

    # PROJECTION
    
    if "+proj" in partdict:

        # get predefined proj def
        projname = partdict["+proj"]
        projclass = projections.find(projname, "proj4", strict)
        if projclass:
            proj = projclass()
        elif projname == "longlat":
            # proj4 special case, longlat as projection name means unprojected geogcs
            proj = None
        else:
            raise NotImplementedError("Unsupported projection: The specified projection name %r could not be found in the list of supported projections" % projname)

    else:
        raise FormatError("The format string is missing the required +proj element")

    if proj:

        # Because proj4 has no element hierarchy, using automatic element find() would
        # ...would not be very effective, as that would need a try-fail approach for each
        # ...element type (parameter, projection, datum, ellipsoid, unit).
        # ...Instead load each element individually.

        # CENTRAL MERIDIAN

        if "+lon_0" in partdict:
            val = partdict["+lon_0"]
            obj = parameters.CentralMeridian(val)
            params.append(obj)

        # FALSE EASTING

        if "+x_0" in partdict:
            val = partdict["+x_0"]
            obj = parameters.FalseEasting(val)
            params.append(obj)

        # FALSE NORTHING

        if "+y_0" in partdict:
            val = partdict["+y_0"]
            obj = parameters.FalseNorthing(val)
            params.append(obj)

        # SCALING FACTOR
        
        if "+k_0" in partdict or "+k" in partdict:
            if "+k_0" in partdict: val = partdict["+k_0"]
            elif "+k" in partdict: val = partdict["+k"]
            obj = parameters.ScalingFactor(val)
            params.append(obj)

        # LATITUDE ORIGIN

        if "+lat_0" in partdict:
            val = partdict["+lat_0"]
            obj = parameters.LatitudeOrigin(val)
            params.append(obj)

        # LATITUDE TRUE SCALE

        if "+lat_ts" in partdict:
            val = partdict["+lat_ts"]
            obj = parameters.LatitudeTrueScale(val)
            params.append(obj)

        # LONGITUDE CENTER

        if "+lonc" in partdict:
            val = partdict["+lonc"]
            obj = parameters.LongitudeCenter(val)
            params.append(obj)

        # AZIMUTH

        if "+alpha" in partdict:
            val = partdict["+alpha"]
            obj = parameters.Azimuth(val)
            params.append(obj)

        # STD PARALLEL 1

        if "+lat_1" in partdict:
            val = partdict["+lat_1"]
            obj = parameters.LatitudeFirstStndParallel(val)
            params.append(obj)
            
        # STD PARALLEL 2

        if "+lat_2" in partdict:
            val = partdict["+lat_2"]
            obj = parameters.LatitudeSecondStndParallel(val)
            params.append(obj)
            
        # SATELLITE HEIGHT
        if "+h" in partdict:
            val = partdict["+h"]
            obj = parameters.SatelliteHeight(val)
            params.append(obj)

        # TILT ANGLE
        if "+tilt" in partdict:
            val = partdict["+tilt"]
            obj = parameters.TiltAngle(val)
            params.append(obj)

        # UNIT

        # get values
        if "+units" in partdict:
            # unit name takes precedence over to_meter
            unitname = partdict["+units"]
            unitclass = units.find(unitname, "proj4", strict)
            if unitclass:
                unit = unitclass() # takes meter multiplier from name, ignoring any custom meter multiplier
            else:
                raise FormatError("The specified unit name %r does not appear to be a valid unit name" % unitname)
        elif "+to_meter" in partdict:
            # no unit name specified, only to_meter conversion factor
            unit = units.Unknown()
            unit.metermultiplier.value = partdict["+to_meter"]
        else:
            # if nothing specified, defaults to meter
            unit = units.Meter()

        # PROJCS

        projcs = containers.ProjCS("Unknown", geogcs, proj, params, unit)
        return projcs

    else:
        # means projdef was None, ie unprojected longlat geogcs
        return geogcs
Parse crs as proj4 formatted string or dict and return the resulting crs object.

    Arguments:

    - *proj4*: The proj4 representation as a string or dict.
    - *strict* (optional): When True, the parser is strict about names having to match
        exactly with upper and lowercases. Default is not strict (False).

    Returns:

    - A CS instance of the indicated type.