def writeKmlFile(self, fileDst: str, bExpand=0) -> None: if not self.oGeo: sMsg: str = " file {0} - Empty source geometry".format(fileDst) if self.oLog: self.oLog.info("Unwritten" + sMsg, outConsole=False) else: print(sMsg) bpaTools.deleteFile(fileDst) return self.oKml.write(fileDst, bExpand=bExpand) sMsg: str = "Write file - " + fileDst if self.oLog: self.oLog.info(sMsg, outConsole=False) else: print(sMsg) return
def resetFile(self) -> None: self.closeFile() if not self.isSilent: bpaTools.deleteFile(self.sLogFile) self.__initFile__() return
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 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