def stationTimeZone(stationFeature): timeZoneId = stationFeature['timeZoneId'] timeZoneUTC = stationFeature['timeZoneUTC'] tz = None if timeZoneId: tz = QTimeZone(QByteArray(timeZoneId.encode())) if not tz.isValid(): tz = None if not tz: tz = QTimeZone(QByteArray(timeZoneUTC.encode())) return tz
def convert_to_time_zone(values, feature, parent): """Converts the given datetime to a time zone.<br> <br> convert_to_time_zone(datetime, utcId[, ianaId])<br> <br> datetime -- a datetime to convert<br> utcId -- a required string UTC offset, e.g. 'UTC+01:00' ianaId -- an optional IANA timezone ID, e.g. 'America/Chicago' """ dt = values[0] utcId = values[1] tz = None if len(values) >= 3: ianaId = values[2] if ianaId: tz = QTimeZone(QByteArray(ianaId.encode())) # if we have IANA, we'll try it if not tz.isValid(): tz = None # wasn't available on our system, we guess if not tz: tz = QTimeZone(QByteArray(utcId.encode())) # our fallback position is to use UTC return dt.toTimeZone(tz)
def testExportToSvg(self): md = QgsProject.instance().metadata() md.setTitle('proj title') md.setAuthor('proj author') md.setCreationDateTime(QDateTime(QDate(2011, 5, 3), QTime(9, 4, 5), QTimeZone(36000))) md.setIdentifier('proj identifier') md.setAbstract('proj abstract') md.setKeywords({'kw': ['kw1', 'kw2']}) QgsProject.instance().setMetadata(md) l = QgsLayout(QgsProject.instance()) l.initializeDefaults() # add a second page page2 = QgsLayoutItemPage(l) page2.setPageSize('A5') l.pageCollection().addPage(page2) # add some items item1 = QgsLayoutItemShape(l) item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.green) fill.setStrokeStyle(Qt.NoPen) item1.setSymbol(fill_symbol) l.addItem(item1) item2 = QgsLayoutItemShape(l) item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) item2.attemptMove(QgsLayoutPoint(10, 20), page=1) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.cyan) fill.setStrokeStyle(Qt.NoPen) item2.setSymbol(fill_symbol) l.addItem(item2) exporter = QgsLayoutExporter(l) # setup settings settings = QgsLayoutExporter.SvgExportSettings() settings.dpi = 80 settings.forceVectorOutput = False settings.exportMetadata = True svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvgdpi.svg') svg_file_path_2 = os.path.join(self.basetestpath, 'test_exporttosvgdpi_2.svg') self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) self.assertTrue(os.path.exists(svg_file_path)) self.assertTrue(os.path.exists(svg_file_path_2)) # metadata def checkMetadata(f, expected): # ideally we'd check the path too - but that's very complex given that # the output from Qt svg generator isn't valid XML, and no Python standard library # xml parser handles invalid xml... self.assertEqual('proj title' in open(f).read(), expected) self.assertEqual('proj author' in open(f).read(), expected) self.assertEqual('proj identifier' in open(f).read(), expected) self.assertEqual('2011-05-03' in open(f).read(), expected) self.assertEqual('proj abstract' in open(f).read(), expected) self.assertEqual('kw1' in open(f).read(), expected) self.assertEqual('kw2' in open(f).read(), expected) for f in [svg_file_path, svg_file_path_2]: checkMetadata(f, True) rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttosvgdpi.png') svgToPng(svg_file_path, rendered_page_1, width=936) rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttosvgdpi2.png') svgToPng(svg_file_path_2, rendered_page_2, width=467) self.assertTrue(self.checkImage('exporttosvgdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) self.assertTrue(self.checkImage('exporttosvgdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) # no metadata settings.exportMetadata = False self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) for f in [svg_file_path, svg_file_path_2]: checkMetadata(f, False) # layered settings.exportAsLayers = True settings.exportMetadata = True svg_file_path = os.path.join(self.basetestpath, 'test_exporttosvglayered.svg') svg_file_path_2 = os.path.join(self.basetestpath, 'test_exporttosvglayered_2.svg') self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) self.assertTrue(os.path.exists(svg_file_path)) self.assertTrue(os.path.exists(svg_file_path_2)) rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttosvglayered.png') svgToPng(svg_file_path, rendered_page_1, width=936) rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttosvglayered2.png') svgToPng(svg_file_path_2, rendered_page_2, width=467) self.assertTrue(self.checkImage('exporttosvglayered_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) self.assertTrue(self.checkImage('exporttosvglayered_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) for f in [svg_file_path, svg_file_path_2]: checkMetadata(f, True) # layered no metadata settings.exportAsLayers = True settings.exportMetadata = False self.assertEqual(exporter.exportToSvg(svg_file_path, settings), QgsLayoutExporter.Success) for f in [svg_file_path, svg_file_path_2]: checkMetadata(f, False)
def testExportToPdf(self): md = QgsProject.instance().metadata() md.setTitle('proj title') md.setAuthor('proj author') md.setCreationDateTime(QDateTime(QDate(2011, 5, 3), QTime(9, 4, 5), QTimeZone(36000))) md.setIdentifier('proj identifier') md.setAbstract('proj abstract') md.setKeywords({'kw': ['kw1', 'kw2'], 'KWx': ['kw3', 'kw4']}) QgsProject.instance().setMetadata(md) l = QgsLayout(QgsProject.instance()) l.initializeDefaults() # add a second page page2 = QgsLayoutItemPage(l) page2.setPageSize('A5') l.pageCollection().addPage(page2) # add some items item1 = QgsLayoutItemShape(l) item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.green) fill.setStrokeStyle(Qt.NoPen) item1.setSymbol(fill_symbol) l.addItem(item1) item2 = QgsLayoutItemShape(l) item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) item2.attemptMove(QgsLayoutPoint(10, 20), page=1) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.cyan) fill.setStrokeStyle(Qt.NoPen) item2.setSymbol(fill_symbol) l.addItem(item2) exporter = QgsLayoutExporter(l) # setup settings settings = QgsLayoutExporter.PdfExportSettings() settings.dpi = 80 settings.rasterizeWholeImage = False settings.forceVectorOutput = False settings.exportMetadata = True pdf_file_path = os.path.join(self.basetestpath, 'test_exporttopdfdpi.pdf') self.assertEqual(exporter.exportToPdf(pdf_file_path, settings), QgsLayoutExporter.Success) self.assertTrue(os.path.exists(pdf_file_path)) rendered_page_1 = os.path.join(self.basetestpath, 'test_exporttopdfdpi.png') dpi = 80 pdfToPng(pdf_file_path, rendered_page_1, dpi=dpi, page=1) rendered_page_2 = os.path.join(self.basetestpath, 'test_exporttopdfdpi2.png') pdfToPng(pdf_file_path, rendered_page_2, dpi=dpi, page=2) self.assertTrue(self.checkImage('exporttopdfdpi_page1', 'exporttopdfdpi_page1', rendered_page_1, size_tolerance=1)) self.assertTrue(self.checkImage('exporttopdfdpi_page2', 'exporttopdfdpi_page2', rendered_page_2, size_tolerance=1)) d = gdal.Open(pdf_file_path) metadata = d.GetMetadata() self.assertEqual(metadata['AUTHOR'], 'proj author') self.assertEqual(metadata['CREATION_DATE'], "D:20110503090405+10'0'") self.assertEqual(metadata['KEYWORDS'], 'KWx: kw3,kw4;kw: kw1,kw2') self.assertEqual(metadata['SUBJECT'], 'proj abstract') self.assertEqual(metadata['TITLE'], 'proj title')
def testExportToImage(self): md = QgsProject.instance().metadata() md.setTitle('proj title') md.setAuthor('proj author') md.setCreationDateTime(QDateTime(QDate(2011, 5, 3), QTime(9, 4, 5), QTimeZone(36000))) md.setIdentifier('proj identifier') md.setAbstract('proj abstract') md.setKeywords({'kw': ['kw1', 'kw2'], 'KWx': ['kw3', 'kw4']}) QgsProject.instance().setMetadata(md) l = QgsLayout(QgsProject.instance()) l.initializeDefaults() # add a second page page2 = QgsLayoutItemPage(l) page2.setPageSize('A5') l.pageCollection().addPage(page2) # add some items item1 = QgsLayoutItemShape(l) item1.attemptSetSceneRect(QRectF(10, 20, 100, 150)) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.green) fill.setStrokeStyle(Qt.NoPen) item1.setSymbol(fill_symbol) l.addItem(item1) item2 = QgsLayoutItemShape(l) item2.attemptSetSceneRect(QRectF(10, 20, 100, 150)) item2.attemptMove(QgsLayoutPoint(10, 20), page=1) fill = QgsSimpleFillSymbolLayer() fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill) fill.setColor(Qt.cyan) fill.setStrokeStyle(Qt.NoPen) item2.setSymbol(fill_symbol) l.addItem(item2) exporter = QgsLayoutExporter(l) # setup settings settings = QgsLayoutExporter.ImageExportSettings() settings.dpi = 80 rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagedpi.png') self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) self.assertTrue(self.checkImage('exporttoimagedpi_page1', 'exporttoimagedpi_page1', rendered_file_path)) page2_path = os.path.join(self.basetestpath, 'test_exporttoimagedpi_2.png') self.assertTrue(self.checkImage('exporttoimagedpi_page2', 'exporttoimagedpi_page2', page2_path)) for f in (rendered_file_path, page2_path): d = gdal.Open(f) metadata = d.GetMetadata() self.assertEqual(metadata['Author'], 'proj author') self.assertEqual(metadata['Created'], '2011-05-03T09:04:05+10:00') self.assertEqual(metadata['Keywords'], 'KWx: kw3,kw4;kw: kw1,kw2') self.assertEqual(metadata['Subject'], 'proj abstract') self.assertEqual(metadata['Title'], 'proj title') # crop to contents settings.cropToContents = True settings.cropMargins = QgsMargins(10, 20, 30, 40) rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagecropped.png') self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) self.assertTrue(self.checkImage('exporttoimagecropped_page1', 'exporttoimagecropped_page1', rendered_file_path)) page2_path = os.path.join(self.basetestpath, 'test_exporttoimagecropped_2.png') self.assertTrue(self.checkImage('exporttoimagecropped_page2', 'exporttoimagecropped_page2', page2_path)) # specific pages settings.cropToContents = False settings.pages = [1] rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagepages.png') self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) self.assertFalse(os.path.exists(rendered_file_path)) page2_path = os.path.join(self.basetestpath, 'test_exporttoimagepages_2.png') self.assertTrue(self.checkImage('exporttoimagedpi_page2', 'exporttoimagedpi_page2', page2_path)) # image size settings.imageSize = QSize(600, 851) rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagesize.png') self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) self.assertFalse(os.path.exists(rendered_file_path)) page2_path = os.path.join(self.basetestpath, 'test_exporttoimagesize_2.png') self.assertTrue(self.checkImage('exporttoimagesize_page2', 'exporttoimagesize_page2', page2_path)) # image size with incorrect aspect ratio # this can happen as a result of data defined page sizes settings.imageSize = QSize(851, 600) rendered_file_path = os.path.join(self.basetestpath, 'test_exporttoimagesizebadaspect.png') self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success) page2_path = os.path.join(self.basetestpath, 'test_exporttoimagesizebadaspect_2.png') im = QImage(page2_path) self.assertTrue(self.checkImage('exporttoimagesize_badaspect', 'exporttoimagedpi_page2', page2_path), '{}x{}'.format(im.width(), im.height()))
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) track_id = self.parameterAsInt(parameters, self.TRACK_ID, context) track_tags = self.parameterAsString(parameters, self.TRACK_TAGS, context) timestamp_field = self.parameterAsString(parameters, self.TIMESTAMP_FIELD, context) add_track_point_motion_attributes = self.parameterAsBoolean( parameters, self.ADD_MOTION_ATTRIBUTES, context) fields_for_tags = self.parameterAsFields(parameters, self.FIELDS_AS_TAGS, context) json_file = self.parameterAsFileOutput(parameters, self.OUTPUT, context) json_track = { 'id': track_id, 'metadata': dict(), 'trackPoints': list() } meta_number_of_points = 0 meta_start_date = None meta_end_date = None meta_length = 0 meta_tags = None if track_tags != '': meta_tags = json.loads(track_tags) base_timestamp = QDateTime(QDate(1970, 1, 1), QTime(0, 0, 0), QTimeZone.utc()) base_timestamp_ms = base_timestamp.toMSecsSinceEpoch() previous_feature = None previous_track_point = None total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, feature in enumerate(source.getFeatures()): if not feature[timestamp_field]: # Skip feature # feedback.pushInfo("Timestamp not available. Point skipped.") continue if not feature.geometry(): # Skip feature # feedback.pushInfo("Geometry not available. Point skipped.") continue feature_timestamp_ms = feature[timestamp_field].toMSecsSinceEpoch() timestamp = int((feature_timestamp_ms - base_timestamp_ms)) meta_start_date = timestamp if meta_start_date is None else meta_start_date meta_end_date = timestamp meta_number_of_points += 1 track_point = { 'id': meta_number_of_points, 'timestamp': timestamp, 'x': feature.geometry().constGet().x(), 'y': feature.geometry().constGet().y() } if feature.geometry().constGet().is3D(): track_point['z'] = feature.geometry().constGet().z() length = 0.0 if previous_feature: length = GeomTools.distance( previous_feature.geometry().constGet(), feature.geometry().constGet(), source.sourceCrs()) if length is not None: meta_length += length if add_track_point_motion_attributes: if previous_feature: track_point['h'] = GeomTools.calculate_angle( previous_feature.geometry().constGet(), feature.geometry().constGet()) / math.pi * 180 track_point['distCalc'] = length track_point['vCalc'] = GeomTools.calculate_speed( previous_feature[timestamp_field], feature[timestamp_field], previous_feature.geometry().constGet(), feature.geometry().constGet(), source.sourceCrs()) track_point[ 'durationCalc'] = timestamp - previous_track_point[ 'timestamp'] if track_point['vCalc'] and previous_track_point['vCalc']: track_point['aCalc'] = (track_point['vCalc'] / 3.6 - previous_track_point['vCalc'] / 3.6) /\ (track_point['durationCalc'] / 1000) else: track_point['aCalc'] = None else: # first feature track_point['h'] = None track_point['distCalc'] = None track_point['vCalc'] = None track_point['durationCalc'] = None track_point['aCalc'] = None if len(fields_for_tags) > 0: track_point_tags = dict() for field in fields_for_tags: if feature[field]: track_point_tags[field] = feature[field] if len(track_point_tags) > 0: track_point['tags'] = track_point_tags json_track['trackPoints'].append(track_point) feedback.setProgress(int(current * total)) previous_feature = feature previous_track_point = track_point if meta_start_date and meta_end_date: json_track['metadata'][ 'duration'] = meta_end_date - meta_start_date else: json_track['metadata']['duration'] = None json_track['metadata']['startDate'] = meta_start_date json_track['metadata']['endDate'] = meta_end_date json_track['metadata']['length'] = meta_length json_track['metadata']['numberOfPoints'] = meta_number_of_points if meta_tags: json_track['metadata']['tags'] = meta_tags with open(json_file, 'w') as output_file: output_file.write(json.dumps(json_track)) return { self.OUTPUT: json_file, self.NUMBER_TRACK_POINTS: meta_number_of_points }
def test_feature_to_json(self): test_fields = QgsFields() attributes = [] test_fields.append(QgsField('a_string_field', QVariant.String)) attributes.append('my string value') test_fields.append(QgsField('a_int_field', QVariant.Int)) attributes.append(5) test_fields.append(QgsField('a_double_field', QVariant.Double)) attributes.append(5.5) test_fields.append(QgsField('a_boolean_field', QVariant.Bool)) attributes.append(True) test_fields.append(QgsField('a_datetime_field', QVariant.DateTime)) attributes.append( QDateTime(QDate(2022, 3, 4), QTime(12, 13, 14), Qt.UTC)) test_fields.append(QgsField('a_date_field', QVariant.Date)) attributes.append(QDate(2022, 3, 4)) test_fields.append(QgsField('a_null_value', QVariant.String)) attributes.append(NULL) test_feature = QgsFeature(test_fields) test_feature.setAttributes(attributes) test_feature.setGeometry(QgsGeometry.fromWkt('Point(1 2)')) context = QgsArcGisRestContext() context.setTimeZone(QTimeZone.utc()) res = QgsArcGisRestUtils.featureToJson(test_feature, context) self.assertEqual( res, { 'attributes': { 'a_boolean_field': True, 'a_datetime_field': 1646395994000, 'a_date_field': 1646352000000, 'a_double_field': 5.5, 'a_int_field': 5, 'a_string_field': 'my string value', 'a_null_value': None }, 'geometry': { 'x': 1.0, 'y': 2.0 } }) # without geometry res = QgsArcGisRestUtils.featureToJson( test_feature, context, flags=QgsArcGisRestUtils.FeatureToJsonFlags( QgsArcGisRestUtils.FeatureToJsonFlag. IncludeNonObjectIdAttributes)) self.assertEqual( res, { 'attributes': { 'a_boolean_field': True, 'a_datetime_field': 1646395994000, 'a_date_field': 1646352000000, 'a_double_field': 5.5, 'a_int_field': 5, 'a_string_field': 'my string value', 'a_null_value': None } }) # without attributes context.setObjectIdFieldName('a_int_field') res = QgsArcGisRestUtils.featureToJson( test_feature, context, flags=QgsArcGisRestUtils.FeatureToJsonFlags( QgsArcGisRestUtils.FeatureToJsonFlag.IncludeGeometry)) self.assertEqual(res, { 'attributes': { 'a_int_field': 5 }, 'geometry': { 'x': 1.0, 'y': 2.0 } })