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
def getAirspaceFunctionalKey(self, airspaceProperties:dict) -> str: sKey = "{0}@{1}".format(self.getAirspaceFunctionalKeyName(airspaceProperties), aixmReader.getSerializeAlt(airspaceProperties)) return sKey
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
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
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