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 } })