"existing tags" temp profile when editing, "existing operator" profile when such tags exist, full editing of existing nodes via refine tags

This commit is contained in:
stopflock
2026-02-01 14:50:47 -06:00
parent 1dd0258c0b
commit ba3b844c1e
19 changed files with 521 additions and 100 deletions
+27
View File
@@ -1,4 +1,5 @@
import 'package:uuid/uuid.dart';
import 'osm_node.dart';
/// Sentinel value for copyWith methods to distinguish between null and not provided
const Object _notProvided = Object();
@@ -264,5 +265,31 @@ class NodeProfile {
@override
int get hashCode => id.hashCode;
/// Create a temporary profile representing the existing tags on a node (minus direction and operator)
/// Used as the default "<Existing tags>" option when editing nodes
static NodeProfile createExistingTagsProfile(OsmNode node) {
final tagsWithoutSpecial = Map<String, String>.from(node.tags);
// Remove direction tags (handled separately)
tagsWithoutSpecial.remove('direction');
tagsWithoutSpecial.remove('camera:direction');
// Remove operator tags (handled separately by operator profile)
tagsWithoutSpecial.removeWhere((key, value) =>
key == 'operator' || key.startsWith('operator:'));
return NodeProfile(
id: 'temp-no-change-${node.id}',
name: '<Existing tags>', // Will be localized in UI
tags: tagsWithoutSpecial,
builtin: false,
requiresDirection: true,
submittable: true,
editable: false,
);
}
/// Returns true if this is a temporary "existing tags" profile
bool get isExistingTagsProfile => id.startsWith('temp-no-change-');
}
+53
View File
@@ -1,4 +1,5 @@
import 'package:uuid/uuid.dart';
import 'osm_node.dart';
/// A bundle of OSM tags that describe a particular surveillance operator.
/// These are applied on top of camera profile tags during submissions.
@@ -76,4 +77,56 @@ class OperatorProfile {
@override
int get hashCode => id.hashCode;
/// Create a temporary operator profile from existing operator tags on a node
/// First tries to match against saved operator profiles, otherwise creates temporary one
/// Used as the default operator profile when editing nodes
static OperatorProfile? createExistingOperatorProfile(OsmNode node, List<OperatorProfile> savedProfiles) {
final operatorTags = _extractOperatorTags(node.tags);
if (operatorTags.isEmpty) return null;
// First, try to find a perfect match among saved profiles
for (final savedProfile in savedProfiles) {
if (_tagsMatch(savedProfile.tags, operatorTags)) {
return savedProfile;
}
}
// No perfect match found, create temporary profile
final operatorName = operatorTags['operator'] ?? '<existing>';
return OperatorProfile(
id: 'temp-existing-operator-${node.id}',
name: operatorName,
tags: operatorTags,
);
}
/// Check if two tag maps are identical
static bool _tagsMatch(Map<String, String> tags1, Map<String, String> tags2) {
if (tags1.length != tags2.length) return false;
for (final entry in tags1.entries) {
if (tags2[entry.key] != entry.value) return false;
}
return true;
}
/// Extract all operator-related tags from a node's tags
static Map<String, String> _extractOperatorTags(Map<String, String> tags) {
final operatorTags = <String, String>{};
for (final entry in tags.entries) {
// Include operator= and any operator:*= tags
if (entry.key == 'operator' || entry.key.startsWith('operator:')) {
operatorTags[entry.key] = entry.value;
}
}
return operatorTags;
}
/// Returns true if this is a temporary "existing operator" profile
bool get isExistingOperatorProfile => id.startsWith('temp-existing-operator-');
}