Ejemplo n.º 1
0
def makeOpenair(oAirspace:dict, gpsType:str) -> list:
    openair = []
    oZone = oAirspace.get("properties", oAirspace)
    theClass = oZone["class"]
    theType = oZone["type"]

    #1/ Specific translations for Openair format
    if theClass=="D" and theType=="CTR":    theClass="CTR"     #CTR CONTROL TRAFFIC AREAS
    #2/ Specific translations for Openair format
    if   theType=="RMZ":                    theClass="RMZ"
    elif theType=="TMZ":                    theClass="TMZ"

    openair.append("AC {0}".format(theClass))
    openair.append("AN {0}".format(oZone["nameV"]))
    openair.append('*AAlt ["{0}", "{1}"]'.format(aixmReader.getSerializeAlt(oZone)[1:-1], aixmReader.getSerializeAltM(oZone)[1:-1]))
    openair.append("*AUID GUId={0} UId={1} Id={2}".format(oZone.get("GUId", "!"), oZone["UId"], oZone["id"]))
    if "desc" in oZone:     openair.append("*ADescr {0}".format(oZone["desc"]))
    if "Mhz" in oZone:      openair.append("*AMhz {0}".format(json.dumps(oZone["Mhz"])))
    if ("activationCode" in oZone) and ("activationDesc" in oZone):       openair.append("*AActiv [{0}] {1}".format(oZone["activationCode"], oZone["activationDesc"]))
    if ("activationCode" in oZone) and not ("activationDesc" in oZone):   openair.append("*AActiv [{0}]".format(oZone["activationCode"]))
    if not("activationCode" in oZone) and ("activationDesc" in oZone):    openair.append("*AActiv {0}".format(oZone["activationDesc"]))
    if "timeScheduling" in oZone:   openair.append("*ATimes {0}".format(json.dumps(oZone["timeScheduling"])))
    if "exceptSAT" in oZone:        openair.append("*AExSAT {0}".format(oZone["exceptSAT"]))
    if "exceptSUN" in oZone:        openair.append("*AExSUN {0}".format(oZone["exceptSUN"]))
    if "exceptHOL" in oZone:        openair.append("*AExHOL {0}".format(oZone["exceptHOL"]))
    if "seeNOTAM" in oZone:         openair.append("*ASeeNOTAM {0}".format(oZone["seeNOTAM"]))
    openair.append("AH {0}".format(parseAlt("AH", gpsType, oZone)))
    if "ordinalUpperMaxM" in oZone:     openair.append("*AH2 {0}".format(oZone["upperMax"]))
    openair.append("AL {0}".format(parseAlt("AL", gpsType, oZone)))
    if "ordinalLowerMinM" in oZone:     openair.append("*AL2 {0}".format(oZone["lowerMin"]))
    if "geometry" in oAirspace:
        openair += oAirspace["geometry"]
    return openair
Ejemplo n.º 2
0
 def getAirspaceFunctionalKey(self, airspaceProperties:dict) -> str:
     sKey = "{0}@{1}".format(self.getAirspaceFunctionalKeyName(airspaceProperties), aixmReader.getSerializeAlt(airspaceProperties))
     return sKey
Ejemplo n.º 3
0
    def saveGeoJsonAirspacesFile(self,
                                 sFile: str,
                                 sContext: str = "all",
                                 sAreaKey: str = None,
                                 epsilonReduce: float = None) -> None:
        oGeoFeatures: list = []
        sContent: str = ""
        oNewHeader: dict = deepcopy(self.oAsCat.oGlobalCatalogHeader)

        #bpaTools.writeJsonFile(sFile + "-tmp.json", self.oGlobalGeoJSON)                       #Sérialisation pour mise au point
        oGlobalCats = self.oAsCat.oGlobalCatalog[
            airspacesCatalog.
            cstKeyCatalogCatalog]  #Récupération de la liste des zones consolidés
        sTitle = "GeoJSON save airspaces file - {0} / {1}".format(
            sContext, sAreaKey)
        barre = bpaTools.ProgressBar(len(oGlobalCats), 20, title=sTitle)
        idx = 0
        for sGlobalKey, oGlobalCat in oGlobalCats.items(
        ):  #Traitement du catalogue global complet
            idx += 1

            #if oGlobalCat["id"] in ["TMA16169","TMA16170"]:
            #    print(oGlobalCat["id"])

            #Filtrage des zones par typologie de sorties
            bIsInclude: bool = False
            if sContext == "ifr":
                bIsInclude = (not oGlobalCat["vfrZone"]) and (
                    not oGlobalCat["groupZone"])
                sContent = "ifrZone"
                sFile = sFile.replace("-all", "-ifr")
            elif sContext == "vfr":
                bIsInclude = oGlobalCat["vfrZone"]
                bIsInclude = bIsInclude or oGlobalCat.get(
                    "vfrZoneExt", False
                )  #Exporter l'extension de vol possible en VFR de 0m jusqu'au FL195/5944m
                sContent = "vfrZone"
                sFile = sFile.replace("-all", "-vfr")
            elif sContext in ["ff", "wrn"]:
                bIsIncludeLoc: bool = True
                if sAreaKey != None:
                    sKey4Find: str = sAreaKey.replace("geo", "ExtOf")
                    if sAreaKey[:
                                9] == "geoFrench":  #Spec for all french territories
                        sKey4Find = "ExtOfFrench"
                    if sKey4Find in oGlobalCat:
                        bIsIncludeLoc = not oGlobalCat[
                            sKey4Find]  #Exclusion de zone
                bIsInclude = bIsIncludeLoc and oGlobalCat["freeFlightZone"]
                #Relevage du plafond de carte pour certaines zones situées en France
                if bIsIncludeLoc and ("freeFlightZoneExt" in oGlobalCat):
                    aFrLocation = [
                        "geoFrenchAlps", "geoFrenchVosgesJura",
                        "geoFrenchPyrenees"
                    ]
                    bIsExtAlt4Loc: bool = False
                    for sLoc in aFrLocation:
                        if oGlobalCat.get(sLoc, False):
                            bIsExtAlt4Loc = True
                            break
                    if bIsExtAlt4Loc:
                        bIsInclude = bIsInclude or (
                            bIsIncludeLoc and oGlobalCat["freeFlightZoneExt"])
                if sContext in ["wrn"]:
                    bIsInclude = bIsInclude and oGlobalCat[
                        "class"] == "Q"  #Ne préserver que les zones DANGEREUSEs
                else:
                    bIsInclude = bIsInclude and oGlobalCat[
                        "class"] != "Q"  #Exclusion systématique des zones DANGEREUSEs
                sContent = "freeflightZone"
                sFile = sFile.replace("-all", "-freeflight")
            elif sContext == "cfd":
                bIsIncludeLoc: bool = True
                if "ExtOfFrench" in oGlobalCat:
                    bIsIncludeLoc = not oGlobalCat[
                        "ExtOfFrench"]  #Pour Exclure toutes zones hors de France
                bIsInclude = bIsIncludeLoc and oGlobalCat["freeFlightZone"]
                if "use4cfd" in oGlobalCat:
                    bIsInclude = bIsInclude or oGlobalCat["use4cfd"]
                #Relevage systématique du plafond de la carte
                elif "freeFlightZoneExt" in oGlobalCat:
                    bIsInclude = bIsInclude or (
                        bIsIncludeLoc and oGlobalCat["freeFlightZoneExt"])
                bIsInclude = bIsInclude and oGlobalCat[
                    "class"] != "Q"  #Exclusion systématique des zones DANGEREUSEs
                sContent = "freeflightZone for FFVL-CFD"
                sFile = sFile.replace("-all", "-ffvl-cfd")
                sAreaKey = ""
            else:
                sContext = "all"
                sContent = "allZone"
                bIsInclude = True

            #Exclude area if unkwown coordonnees
            if bIsInclude and sContext != "all" and "excludeAirspaceNotCoord" in oGlobalCat:
                if oGlobalCat["excludeAirspaceNotCoord"]: bIsInclude = False

            #Filtrage des zones par régionalisation
            bIsArea: bool = True
            if sContext == "cfd":
                bIsArea = oGlobalCat.get(
                    "geoFrenchAll",
                    False)  #Filtrage sur la totalité des territoires Français

            #Maintenir ou Supprimer la LTA-France1 (originale ou spécifique) des cartes non-concernées par le territoire Français --> [D] LTA FRANCE 1 (Id=LTA13071) [FL115-FL195]
            elif oGlobalCat["id"] in ["LTA13071", "BpFrenchSS"]:
                if not sAreaKey in [None, "geoFrench", "geoFrenchAll"]:
                    bIsArea = False

            elif bIsInclude and sAreaKey:
                if sAreaKey == cstWithoutLocation:
                    #Identification des zones non-retenues dans aucun des filtrages géographique paramétrés
                    bIsArea = False
                    for sAreaKey2 in self.oGeoRefArea.AreasRef.keys():
                        if sAreaKey2 in oGlobalCat:
                            bIsArea = bIsArea or oGlobalCat[sAreaKey2]
                        if bIsArea: break
                    bIsArea = not bIsArea
                elif sAreaKey == cstDeltaExtended:
                    bIsArea = oGlobalCat.get("deltaExt", False)
                elif sAreaKey == cstFreeFlightZoneExt:
                    bIsArea = oGlobalCat.get("freeFlightZoneExt", False)
                elif sAreaKey in oGlobalCat:
                    bIsArea = oGlobalCat[sAreaKey]
                else:
                    bIsArea = False

            if not bIsArea:
                sKey4Find: str = sAreaKey.replace(
                    "geo", "IncOf")  #test d'inclusion volontaire de la zone ?
                bIsArea = oGlobalCat.get(sKey4Find, False)

            if bIsArea and bIsInclude and (sGlobalKey in self.oGlobalGeoJSON):
                oSingleCat: dict = {}
                if sContext == "cfd":
                    #Single properties for CFD or FlyXC - {"name":"Agen1 119.15","category":"E","bottom":"2000F MSL","bottom_m":0,"top":"FL 65","color":"#bfbf40"}
                    oSingleCat.update({"name": oGlobalCat["nameV"]})
                    oSingleCat.update({"category": oGlobalCat["class"]})
                    oSingleCat.update({"type": oGlobalCat["type"]})
                    oSingleCat.update({
                        "alt":
                        "{0} {1}".format(
                            aixmReader.getSerializeAlt(oGlobalCat),
                            aixmReader.getSerializeAltM(oGlobalCat))
                    })
                    oSingleCat.update({
                        "bottom":
                        aixmReader.getSerializeAlt(oGlobalCat, "Low")
                    })
                    oSingleCat.update(
                        {"top": aixmReader.getSerializeAlt(oGlobalCat, "Upp")})
                    oSingleCat.update({"bottom_m": oGlobalCat["lowerM"]})
                    oSingleCat.update({"top_m": oGlobalCat["upperM"]})
                    oSingleCat.update({
                        "Ids":
                        "GUId={0} UId={1} Id={2}".format(
                            oGlobalCat.get("GUId", "!"), oGlobalCat["UId"],
                            oGlobalCat["id"])
                    })
                    if "codeActivity" in oGlobalCat:
                        oSingleCat.update(
                            {"codeActivity": oGlobalCat["codeActivity"]})
                    if "exceptSAT" in oGlobalCat:
                        oSingleCat.update(
                            {"exceptSAT": oGlobalCat["exceptSAT"]})
                    if "exceptSUN" in oGlobalCat:
                        oSingleCat.update(
                            {"exceptSUN": oGlobalCat["exceptSUN"]})
                    if "exceptHOL" in oGlobalCat:
                        oSingleCat.update(
                            {"exceptHOL": oGlobalCat["exceptHOL"]})
                    if "seeNOTAM" in oGlobalCat:
                        oSingleCat.update({"seeNOTAM": oGlobalCat["seeNOTAM"]})
                    if "activationCode" in oGlobalCat:
                        oSingleCat.update(
                            {"activationCode": oGlobalCat["activationCode"]})
                    if "activationDesc" in oGlobalCat:
                        oSingleCat.update(
                            {"activationDesc": oGlobalCat["activationDesc"]})
                    if "timeScheduling" in oGlobalCat:
                        oSingleCat.update(
                            {"timeScheduling": oGlobalCat["timeScheduling"]})
                    if "desc" in oGlobalCat:
                        oSingleCat.update({"desc": oGlobalCat["desc"]})
                else:
                    #Extract single parts of properties
                    aSinglePorperties: list = [
                        "nameV", "class", "type", "codeActivity", "lower",
                        "lowerMin", "upper", "upperMax", "ordinalLowerM",
                        "ordinalUpperM", "lowerM", "upperM",
                        "groundEstimatedHeight", "desc", "declassifiable",
                        "activationCode", "activationDesc", "timeScheduling",
                        "Mhz", "GUId", "UId", "id"
                    ]  #Exclude: zoneType, groupZone, srcClass, srcType, vfrZone, vfrZoneExt, freeFlightZone, freeFlightZoneExt, srcName, name; etc...
                    for sProp in aSinglePorperties:
                        value = oGlobalCat.get(sProp, None)
                        if value != None:
                            oSingleCat.update({sProp: value})
                if sContext != "cfd":
                    aixm2json.addColorProperties(
                        oGlobalCat, oSingleCat, self.oLog
                    )  #Ajout des propriétés pour colorisation de la zone

                #Extraction des coordonnées
                oAsGeo = self.oGlobalGeoJSON[sGlobalKey]
                if oAsGeo[poaffCst.cstGeoType].lower() == (
                        poaffCst.cstGeoPoint).lower():
                    oCoords: list = oAsGeo[
                        poaffCst.
                        cstGeoCoordinates]  #get coordinates of geometry
                elif oAsGeo[poaffCst.cstGeoType].lower() == (
                        poaffCst.cstGeoLine).lower():
                    oCoords: list = oAsGeo[
                        poaffCst.
                        cstGeoCoordinates]  #get coordinates of geometry
                elif oAsGeo[poaffCst.cstGeoType].lower() == (
                        poaffCst.cstGeoPolygon).lower():
                    oCoords: list = oAsGeo[poaffCst.cstGeoCoordinates][
                        0]  #get coordinates of geometry

                #Optimisation du tracé GeoJSON
                oCoordsDst: list = []
                #if epsilonReduce<0: --> do not change !
                if epsilonReduce == 0 or (
                        epsilonReduce > 0 and len(oCoords) > 40
                ):  #Ne pas optimiser le tracé des zones ayant moins de 40 segments (préservation des tracés de cercle minimaliste)
                    oCoordsDst = rdp(oCoords, epsilon=epsilonReduce
                                     )  #Optimisation du tracé des coordonnées

                #Remplacement du tracé s'il a été optimisé
                iOrgSize: int = len(oCoords)
                iNewSize: int = len(oCoordsDst)
                if iNewSize > 0 and iNewSize != iOrgSize:
                    percent: float = round((1 - (iNewSize / iOrgSize)) * 100,
                                           1)
                    percent = int(percent) if percent >= 1.0 else percent
                    sOpti: str = "Segments optimisés à {0}% ({1}->{2}) [rdp={3}] ***".format(
                        percent, iOrgSize, iNewSize, epsilonReduce)
                    self.oLog.debug(
                        "GeoJSON RDP Optimisation: {0} - {1}".format(
                            oSingleCat["nameV"], sOpti),
                        level=2,
                        outConsole=False)
                    #self.oCtrl.oLog.debug("RDP Src: {0}".format(oCoords), level=8)
                    #self.oCtrl.oLog.debug("RDP Dst: {0}".format(oCoordsDst), level=8)
                    if oAsGeo[poaffCst.cstGeoType].lower() == (
                            poaffCst.cstGeoPoint).lower():
                        oNewGeo: dict = {
                            poaffCst.cstGeoType: oAsGeo[poaffCst.cstGeoType],
                            poaffCst.cstGeoCoordinates: oCoordsDst
                        }
                    elif oAsGeo[poaffCst.cstGeoType].lower() == (
                            poaffCst.cstGeoLine).lower():
                        oNewGeo: dict = {
                            poaffCst.cstGeoType: oAsGeo[poaffCst.cstGeoType],
                            poaffCst.cstGeoCoordinates: oCoordsDst
                        }
                    elif oAsGeo[poaffCst.cstGeoType].lower() == (
                            poaffCst.cstGeoPolygon).lower():
                        oNewGeo: dict = {
                            poaffCst.cstGeoType: oAsGeo[poaffCst.cstGeoType],
                            poaffCst.cstGeoCoordinates: [oCoordsDst]
                        }
                    oArea: dict = {
                        poaffCst.cstGeoType: poaffCst.cstGeoFeature,
                        poaffCst.cstGeoProperties: oSingleCat,
                        poaffCst.cstGeoGeometry: oNewGeo
                    }
                else:
                    oArea: dict = {
                        poaffCst.cstGeoType: poaffCst.cstGeoFeature,
                        poaffCst.cstGeoProperties: oSingleCat,
                        poaffCst.cstGeoGeometry: oAsGeo
                    }

                oArea: dict = {
                    poaffCst.cstGeoType: poaffCst.cstGeoFeature,
                    poaffCst.cstGeoProperties: oSingleCat,
                    poaffCst.cstGeoGeometry: oAsGeo
                }
                oGeoFeatures.append(oArea)
            barre.update(idx)
        barre.reset()

        if sAreaKey:
            sContent += " / " + sAreaKey
            sFile = sFile.replace(".geojson", "-" + sAreaKey + ".geojson")
        if sContext in ["wrn"]:
            sContent += " / Dangerous areas"
            sFile = sFile.replace(".geojson", "-warning" + ".geojson")

        sMsg: str = " file {0} - {1} areas in map"
        if len(oGeoFeatures) == 0:
            self.oLog.info("GeoJSON unwritten" +
                           sMsg.format(sFile, len(oGeoFeatures)),
                           outConsole=False)
            bpaTools.deleteFile(sFile)
        else:
            self.oLog.info("GeoJSON write" +
                           sMsg.format(sFile, len(oGeoFeatures)),
                           outConsole=False)
            oSrcFiles = oNewHeader.pop(airspacesCatalog.cstKeyCatalogSrcFiles)
            oNewHeader.update(
                {airspacesCatalog.cstKeyCatalogContent: sContent})
            if sAreaKey in self.oGeoRefArea.AreasRef:
                sAreaDesc: str = self.oGeoRefArea.AreasRef[sAreaKey][2]
                oNewHeader.update(
                    {airspacesCatalog.cstKeyCatalogKeyAreaDesc: sAreaDesc})
            del oNewHeader[airspacesCatalog.cstKeyCatalogNbAreas]
            oNewHeader.update(
                {airspacesCatalog.cstKeyCatalogNbAreas: len(oGeoFeatures)})
            oNewHeader.update(
                {airspacesCatalog.cstKeyCatalogSrcFiles: oSrcFiles})
            self.oOutGeoJSON = {}  #Output reset
            self.oOutGeoJSON.update({
                poaffCst.cstGeoType: poaffCst.cstGeoFeatureCol,
                poaffCst.cstGeoHeaderFile: oNewHeader,
                poaffCst.cstGeoFeatures: oGeoFeatures
            })
            bpaTools.writeJsonFile(sFile,
                                   self.oOutGeoJSON)  #Sérialisation du fichier
        return
Ejemplo n.º 4
0
def makeOpenair(oAirspace: dict,
                gpsType: str,
                digit: float = -1,
                epsilonReduce: float = -1,
                nbMaxSegment: int = -1,
                epsilonrDyn: bool = False,
                oLog: bpaTools.Logger = None) -> list:
    openair: list = []
    oZone: dict = oAirspace.get("properties", oAirspace)
    theClass: str = oZone["class"]
    theName: str = oZone["nameV"]

    #theType = oZone["type"]
    #1/ Specific translations for Openair format
    #if theClass=="D" and theType=="CTR":    theClass="CTR"     #CTR CONTROL TRAFFIC AREAS
    #2/ Specific translations for Openair format
    #if   theType=="RMZ":                    theClass="RMZ"
    #elif theType=="TMZ":                    theClass="TMZ"

    openair.append("AC {0}".format(theClass))
    openair.append("AN {0}".format(theName))
    #old openair.append('*AAlt ["{0}", "{1}"]'.format(aixmReader.getSerializeAlt(oZone)[1:-1], aixmReader.getSerializeAltM(oZone)[1:-1]))
    aAlt: list = []
    aAlt.append("{0}".format(aixmReader.getSerializeAlt(oZone)[1:-1]))
    aAlt.append("{0}".format(aixmReader.getSerializeAltM(oZone)[1:-1]))
    if "freeFlightZoneExt" in oZone:
        if oZone["freeFlightZoneExt"] and (not oZone["freeFlightZone"]):
            aAlt.append("ffExt=Yes")
    #if "lowerM" in oZone:
    #    if float(oZone.get("lowerM", 0)) > 3504:  #FL115 = 3505m
    #        aAlt.append("ffExt=Yes")
    if len(aAlt) == 3:
        openair.append('*AAlt ["{0}", "{1}", "{2}"]'.format(
            aAlt[0], aAlt[1], aAlt[2]))
    else:
        openair.append('*AAlt ["{0}", "{1}"]'.format(aAlt[0], aAlt[1]))

    sGUId: str = oZone.get("srcGUId", None)
    if sGUId == None: sGUId = oZone.get("GUId", "!")
    sUId: str = oZone.get("srcUId", None)
    if sUId == None: sUId: str = oZone.get("UId", "!")
    sId: str = oZone.get("id", "!")
    openair.append("*AUID GUId={0} UId={1} Id={2}".format(sGUId, sUId, sId))

    if "desc" in oZone: openair.append("*ADescr {0}".format(oZone["desc"]))
    if "Mhz" in oZone:
        if isinstance(oZone["Mhz"], str):
            sDict: str = bpaTools.getContentOf(oZone["Mhz"],
                                               "{",
                                               "}",
                                               bRetSep=True)
            oAMhz: dict = json.loads(sDict)
        elif isinstance(oZone["Mhz"], dict):
            oAMhz: dict = oZone["Mhz"]
        else:
            oAMhz: dict = None
        openair.append("*AMhz {0}".format(json.dumps(oAMhz,
                                                     ensure_ascii=False)))
    if ("activationCode" in oZone) and ("activationDesc" in oZone):
        openair.append("*AActiv [{0}] {1}".format(oZone["activationCode"],
                                                  oZone["activationDesc"]))
    if ("activationCode" in oZone) and not ("activationDesc" in oZone):
        openair.append("*AActiv [{0}]".format(oZone["activationCode"]))
    if not ("activationCode" in oZone) and ("activationDesc" in oZone):
        openair.append("*AActiv {0}".format(oZone["activationDesc"]))
    if bool(oZone.get("declassifiable", False)): openair.append("*ADecla Yes")
    if "timeScheduling" in oZone:
        openair.append("*ATimes {0}".format(
            json.dumps(oZone["timeScheduling"], ensure_ascii=False)))
    if bool(oZone.get("exceptSAT", False)): openair.append("*AExSAT Yes")
    if bool(oZone.get("exceptSUN", False)): openair.append("*AExSUN Yes")
    if bool(oZone.get("exceptHOL", False)): openair.append("*AExHOL Yes")
    if bool(oZone.get("seeNOTAM", False)): openair.append("*ASeeNOTAM Yes")
    openair.append("AH {0}".format(parseAlt("AH", gpsType, oZone)))
    if oZone.get("ordinalUpperMaxM", False):
        openair.append("*AH2 {0}".format(oZone["upperMax"]))
    openair.append("AL {0}".format(parseAlt("AL", gpsType, oZone)))
    if oZone.get("ordinalLowerMinM", False):
        openair.append("*AL2 {0}".format(oZone["lowerMin"]))

    #Récupération des tracés de base
    oaMap: list = []
    if cstGeometry in oAirspace:
        oaMap = oAirspace[cstGeometry]
    iOrgSize: int = __getNbSegments(oaMap)

    #if theName in ["R 222 B (SeeNotam)"]:
    #    print("map")

    #Optimisation contextuelle des tracés
    #if epsilonReduce<0: --> do not change oaMap !
    if iOrgSize > 9 and epsilonReduce >= 0:  #Ne pas optimiser les petites zones (ou cercles n'ayants que 2 segments ex: 'V X=49:16:30N 4:45:20E' + 'DC 1.0')
        if epsilonReduce == 0 and epsilonrDyn and (
                theClass in ["GP", "ZSM", "G", "E", "Q", "FFVL", "FFVP"]
                or theName[:10] == "LTA FRANCE"):
            epsilonReduce = 0.0001  #Imposer une optimisation pour certaines zones

        if iOrgSize > 6000:
            epsilonReduce = round(
                epsilonReduce * 16, 6
            )  #0.0 standard || 0.0001->0.0016 filtre || 0.0004->0.0096 région
        elif iOrgSize > 4000:
            epsilonReduce = round(
                epsilonReduce * 14, 6
            )  #0.0 standard || 0.0001->0.0014 filtre || 0.0004->0.0084 région
        elif iOrgSize > 3000:
            epsilonReduce = round(
                epsilonReduce * 12, 6
            )  #0.0 standard || 0.0001->0.0012 filtre || 0.0004->0.0072 région
        elif iOrgSize > 2000:
            epsilonReduce = round(
                epsilonReduce * 10, 6
            )  #0.0 standard || 0.0001->0.0010 filtre || 0.0004->0.0060 région
        elif iOrgSize > 1000:
            epsilonReduce = round(
                epsilonReduce * 8, 6
            )  #0.0 standard || 0.0001->0.0008 filtre || 0.0004->0.0048 région
        elif iOrgSize > 600:
            epsilonReduce = round(
                epsilonReduce * 6, 6
            )  #0.0 standard || 0.0001->0.0006 filtre || 0.0004->0.0036 région
        elif iOrgSize > 400:
            epsilonReduce = round(
                epsilonReduce * 4, 6
            )  #0.0 standard || 0.0001->0.0004 filtre || 0.0004->0.0024 région
        elif iOrgSize > 200:
            epsilonReduce = round(
                epsilonReduce * 2, 6
            )  #0.0 standard || 0.0001->0.0002 filtre || 0.0004->0.0012 région
        elif iOrgSize < 20:
            epsilonReduce = 0.0  #0.0 imposer la suppression de doublons
        #else:  #Cas 20 <= iOrgSize < 200
        #    Ne pas changer le coef 'epsilonReduce'     #0.0 standard || 0.0001 filtre || 0.0006 région
        oaMap = __optimizeMap(oaMap, digit, epsilonReduce, oLog)

    iNewSize: int = __getNbSegments(oaMap)
    iCount: int = 0
    if nbMaxSegment > 0 and iNewSize > nbMaxSegment:
        #Cas spécifique d'une demande de limitation d'un nombre maximal de segment
        iDelta: int = iNewSize - nbMaxSegment
        orgEpsilonReduce: float = epsilonReduce
        #oLog.debug("RDP Optimisation Delta: {0} - count={1} rdp={2} / orgSize{3} newSize={4} (delta={5})".format(theName, iCount, epsilonReduce, iOrgSize, iNewSize, iDelta), level=1, outConsole=False)
        while iCount < 50 and iDelta > 0:
            iCount += 1  #End While: iCount==50
            epsilonReduce: float = round(
                orgEpsilonReduce * (1 + (iCount * 0.2)),
                6)  #End while: 0.002*(1 + (0.2*50)) = 0.022
            oaMap = __optimizeMap(oaMap, digit, epsilonReduce, oLog)
            iNewSize = __getNbSegments(oaMap)
            iDelta = iNewSize - nbMaxSegment
            #oLog.debug("RDP Optimisation Delta: {0} - count={1} rdp={2} / orgSize{3} newSize={4} (delta={5})".format(theName, iCount, epsilonReduce, iOrgSize, iNewSize, iDelta), level=1, outConsole=False)

    if iNewSize > 0 and iNewSize != iOrgSize:
        if oLog:
            percent: float = round((1 - (iNewSize / iOrgSize)) * 100, 1)
            percent = int(percent) if percent >= 1.0 else percent
            if iCount > 0:
                sOpti: str = "Optimisés à {0}% ({1}->{2}) [rdp={3}, iCount={4}]".format(
                    percent, iOrgSize, iNewSize, epsilonReduce, iCount)
            else:
                sOpti: str = "Optimisés à {0}% ({1}->{2}) [rdp={3}]".format(
                    percent, iOrgSize, iNewSize, epsilonReduce)
            if oaMap[0][:len(openairAixmSegmnt)] == openairAixmSegmnt:
                sOpti = oaMap[0] + " // " + sOpti
            #Sortie dans le log uniquement en debug level>0
            oLog.debug("Openair RDP Optimisation: {0} - {1}".format(
                theName, sOpti),
                       level=1,
                       outConsole=False)
            #Sortie dans l'Openair uniquement en debug level>0
            if oLog.debugLevel > 0:
                if oaMap[0][:len(openairAixmSegmnt)] == openairAixmSegmnt:
                    oaMap[0] = sOpti
                else:
                    openair.append(openairAixmSegmnt + sOpti)
    else:
        #Sortie dans l'Openair uniquement en debug level>1
        if oLog.debugLevel > 1:
            if oaMap[0][:len(openairAixmSegmnt)] != openairAixmSegmnt:
                openair.append(openairAixmSegmnt + "{0}".format(iOrgSize))

    openair += oaMap
    return openair
Ejemplo n.º 5
0
    def saveOpenairAirspacesFile2(self,
                                  sFile: str,
                                  sContext: str = "all",
                                  gpsType: str = "",
                                  exceptDay: str = "",
                                  sAreaKey: str = "") -> None:
        oOutOpenair: list = []
        sContent: str = ""
        aAddHeader: list = []
        lNbExcludeZone: int = 0

        oGlobalHeader = self.oAsCat.oGlobalCatalog[
            poaffCst.
            cstGeoHeaderFile]  #Récupération de l'entete du catalogue global
        oNewHeader: dict = deepcopy(self.oAsCat.oGlobalCatalogHeader)

        oGlobalCats = self.oAsCat.oGlobalCatalog[
            airspacesCatalog.
            cstKeyCatalogCatalog]  #Récupération de la liste des zones consolidés
        sTitle = "Openair save airspaces file - {0} / {1} / {2} / {3}".format(
            sContext, gpsType, exceptDay, sAreaKey)
        barre = bpaTools.ProgressBar(len(oGlobalCats), 20, title=sTitle)
        idx = 0
        for sGlobalKey, oGlobalCat in oGlobalCats.items(
        ):  #Traitement du catalogue global complet
            idx += 1

            #if oGlobalCat["id"] in ["TMA16169","TMA16170"]:
            #    print(oGlobalCat["id"])

            #Filtrage des zones par typologie de sorties
            bIsInclude: bool = False
            if sContext == "ifr":
                bIsInclude = (not oGlobalCat["vfrZone"]) and (
                    not oGlobalCat["groupZone"])
                sContent = "ifrZone"
                sFile = sFile.replace("-all", "-ifr")
            elif sContext == "vfr":
                bIsInclude = oGlobalCat["vfrZone"]
                bIsInclude = bIsInclude or oGlobalCat.get(
                    "vfrZoneExt", False
                )  #Exporter l'extension de vol possible en VFR de 0m jusqu'au FL195/5944m
                sContent = "vfrZone"
                sFile = sFile.replace("-all", "-vfr")
            elif sContext in ["ff", "wrn"]:
                bIsIncludeLoc: bool = True
                if sAreaKey != "":
                    sKey4Find: str = sAreaKey.replace("geo", "ExtOf")
                    if sAreaKey[:
                                9] == "geoFrench":  #Spec for all french territories
                        sKey4Find = "ExtOfFrench"
                    if sKey4Find in oGlobalCat:
                        bIsIncludeLoc = not oGlobalCat[
                            sKey4Find]  #Exclusion de zone
                bIsInclude = bIsIncludeLoc and oGlobalCat["freeFlightZone"]
                #Relevage du plafond de carte pour certaines zones situées en France
                if bIsIncludeLoc and ("freeFlightZoneExt" in oGlobalCat):
                    aFrLocation = [
                        "geoFrenchAlps", "geoFrenchVosgesJura",
                        "geoFrenchPyrenees"
                    ]
                    bIsExtAlt4Loc: bool = False
                    for sLoc in aFrLocation:
                        if oGlobalCat.get(sLoc, False):
                            bIsExtAlt4Loc = True
                            break
                    if bIsExtAlt4Loc:
                        bIsInclude = bIsInclude or (
                            bIsIncludeLoc and oGlobalCat["freeFlightZoneExt"])
                if sContext in ["wrn"]:
                    bIsInclude = bIsInclude and oGlobalCat[
                        "class"] == "Q"  #Ne préserver que les zones DANGEREUSEs
                else:
                    bIsInclude = bIsInclude and oGlobalCat[
                        "class"] != "Q"  #Exclusion systématique des zones DANGEREUSEs
                sContent = "freeflightZone"
                sFile = sFile.replace("-all", "-freeflight")
            elif sContext == "cfd":
                bIsIncludeLoc: bool = True
                if "ExtOfFrench" in oGlobalCat:
                    bIsIncludeLoc = not oGlobalCat[
                        "ExtOfFrench"]  #Pour Exclure toutes zones hors de France
                bIsInclude = bIsIncludeLoc and oGlobalCat["freeFlightZone"]
                if "use4cfd" in oGlobalCat:
                    bIsInclude = bIsInclude or oGlobalCat["use4cfd"]
                #Relevage systématique du plafond de la carte
                elif "freeFlightZoneExt" in oGlobalCat:
                    bIsInclude = bIsInclude or (
                        bIsIncludeLoc and oGlobalCat["freeFlightZoneExt"])
                bIsInclude = bIsInclude and oGlobalCat[
                    "class"] != "Q"  #Exclusion systématique des zones DANGEREUSEs
                sContent = "freeflightZone for FFVL-CFD"
                sFile = sFile.replace("-all", "-ffvl-cfd")
                sAreaKey = ""
            else:
                sContext = "all"
                sContent = "allZone"
                bIsInclude = True

            #Exclude area if unkwown coordonnees
            if bIsInclude and "excludeAirspaceNotCoord" in oGlobalCat:
                if oGlobalCat["excludeAirspaceNotCoord"]: bIsInclude = False

            #Filtrage des zones par régionalisation
            bIsArea: bool = True
            if sContext == "cfd":
                bIsArea = oGlobalCat.get(
                    "geoFrenchAll",
                    False)  #Filtrage sur la totalité des territoires Français

            elif bIsInclude and sAreaKey:
                if sAreaKey in oGlobalCat:
                    bIsArea = oGlobalCat[sAreaKey]
                else:
                    bIsArea = False

                #Maintenir ou Supprimer la LTA-France1 (originale ou spécifique) des cartes non-concernées par le territoire Français --> [D] LTA FRANCE 1 (Id=LTA13071) [FL115-FL195]
                if bIsArea and oGlobalCat["id"] in ["LTA13071", "BpFrenchSS"]:
                    if sAreaKey in ["", "geoFrench", "geoFrenchAll"]:
                        aAddHeader.append(
                            "'{0}' {1} - Symbolisation de la surface 'S' - Afin de simplifier cette carte, vous pouvez éventuellement supprimer cette couche limite du vol-libre (hors masifs-montagneux...)"
                            .format(oGlobalCat["nameV"],
                                    aixmReader.getSerializeAlt(oGlobalCat)))
                    else:
                        bIsArea = False  #Ne pas afficher cette zone incohérente pour ces régions

            if not bIsArea:
                sKey4Find: str = sAreaKey.replace(
                    "geo", "IncOf")  #test d'inclusion volontaire de la zone ?
                bIsArea = oGlobalCat.get(sKey4Find, False)

            #Filtrage des zones par jour d'activation
            if bIsInclude and bIsArea and exceptDay:
                if exceptDay in oGlobalCat:
                    bIsInclude = False
                    lNbExcludeZone += 1

            if bIsArea and bIsInclude and (sGlobalKey in self.oGlobalOpenair):
                oAs: OpenairZone = self.oGlobalOpenair[sGlobalKey]
                if len(oAs.oBorder) == 1:
                    None  #Exclure tout les points fixes - or (oAs.oBorder[0]!=errLocalisationPoint)
                elif len(oAs.oBorder) == 2 and oAs.oBorder[0][:4] != "V X=":
                    None  #Exclure les doubles points fixes (DP.. + DP..) mais autoriser les cercles (V X=.. + DP..)
                else:
                    oOutOpenair.append(oAs)
            barre.update(idx)
        barre.reset()

        if gpsType == "-gpsWithTopo":
            sContent += " / " + gpsType[1:]
        elif gpsType == "-gpsWithoutTopo":
            sContent += " / " + gpsType[1:]
            sFile = sFile.replace("-gpsWithTopo", "-gpsWithoutTopo")
        else:
            sFile += "_err"

        if sAreaKey:
            sContent += " / " + sAreaKey
            sFile = sFile.replace(".txt", "-" + sAreaKey + ".txt")
        if sContext in ["wrn"]:
            sContent += " / Dangerous areas"
            sFile = sFile.replace(".txt", "-warning" + ".txt")

        if exceptDay:
            ext4exceptDay = exceptDay.replace("except", "for")
            sContent += " / " + ext4exceptDay
            sFile = sFile.replace(".txt", "-" + ext4exceptDay + ".txt")
            if lNbExcludeZone == 0:
                oOutOpenair = [
                ]  #Annulation de la sortie cause fichier non-utile (pas de différentiel d'exclusion de zones)

        sMsg: str = " file {0} - {1} areas in map".format(
            sFile, len(oOutOpenair))
        if len(oOutOpenair) == 0:
            self.oLog.info("Openair unwritten" + sMsg, outConsole=False)
            bpaTools.deleteFile(sFile)
        else:
            self.oLog.info("Openair write" + sMsg, outConsole=False)

            #Entête des fichiers
            oSrcFiles = oNewHeader.pop(airspacesCatalog.cstKeyCatalogSrcFiles)
            oNewHeader.update(
                {airspacesCatalog.cstKeyCatalogContent: sContent})
            sAreaDesc: str = ""
            if sAreaKey in self.oGeoRefArea.AreasRef:
                sAreaDesc: str = self.oGeoRefArea.AreasRef[sAreaKey][2]
                oNewHeader.update(
                    {airspacesCatalog.cstKeyCatalogKeyAreaDesc: sAreaDesc})

            del oNewHeader[airspacesCatalog.cstKeyCatalogNbAreas]
            oNewHeader.update(
                {airspacesCatalog.cstKeyCatalogNbAreas: len(oOutOpenair)})
            oNewHeader.update(
                {airspacesCatalog.cstKeyCatalogSrcFiles: oSrcFiles})

            #Contexte pour optimisation du tracé
            digit: float = poaffCst.cstOpenairDigitOptimize
            epsilonReduce: float = poaffCst.cstOpenairEpsilonReduce  #Param d'optimisation standard de KML
            nbMaxSegment: int = -1  #Nombre maxi de segment admissible (nécessaire pour génération des fichiers FAF)
            if sFile.find("ffvl-cfd") > 0:
                epsilonReduce = poaffCst.cstOpenairCfdEpsilonReduce  #Imposer l'optimisation pour les sorties Openair CFD
            else:
                aToken = [
                    "geoFrenchNorth", "geoFrenchSouth", "geoFrenchNESW",
                    "geoFrenchVosgesJura", "geoFrenchPyrenees", "geoFrenchAlps"
                ]
                if sAreaKey in aToken:
                    epsilonReduce = poaffCst.cstOpenairEpsilonReduceMR  #Optimisation moyenne résolution pour les sorties Openair régionnales
                    nbMaxSegment = 100  #100 segments maxi pour création des fichiers FAF

            oTools = aixmReader.AixmTools(None)
            sOutOpenair: str = oTools.makeHeaderOpenairFile(
                oNewHeader,
                oOutOpenair,
                sContext,
                gpsType,
                exceptDay,
                sAreaKey,
                sAreaDesc,
                aAddHeader,
                digit=digit,
                epsilonReduce=epsilonReduce)

            #Sérialisation de toutes les zones
            oOp: OpenairArea = None
            for oOp in oOutOpenair:
                sOutOpenair += oOp.serializeArea(gpsType, digit, epsilonReduce,
                                                 nbMaxSegment,
                                                 self.oLog) + "\n"

            bpaTools.writeTextFile(sFile,
                                   sOutOpenair)  #Sérialisation du fichier
        return