0
# Changeset Management
1
2
Complete changeset lifecycle including creation, modification, closure, and discussion management. Changesets group related edits together and provide context for modifications to OpenStreetMap data through metadata, comments, and discussion threads.
3
4
## Capabilities
5
6
### Changeset Context Manager
7
8
Automatic changeset management using Python context managers for streamlined editing workflows.
9
10
```python { .api }
11
@contextmanager
12
def Changeset(ChangesetTags={}):
13
"""
14
Context manager for a Changeset that automatically handles opening and closing.
15
16
Parameters:
17
- ChangesetTags (dict, optional): Tags to apply to the changeset
18
19
Returns:
20
int: Changeset ID
21
22
Raises:
23
- UsernamePasswordMissingError: If no authentication provided
24
- ChangesetAlreadyOpenError: If changeset already open
25
"""
26
```
27
28
**Usage Example:**
29
30
```python
31
import osmapi
32
33
api = osmapi.OsmApi(username="your_username", password="your_password")
34
35
# Automatic changeset management
36
with api.Changeset({"comment": "Adding new POIs", "source": "GPS survey"}) as changeset_id:
37
print(f"Working in changeset {changeset_id}")
38
39
# All operations within this block use the same changeset
40
node1 = api.NodeCreate({"lat": 47.6, "lon": -122.3, "tag": {"amenity": "cafe"}})
41
node2 = api.NodeCreate({"lat": 47.61, "lon": -122.31, "tag": {"amenity": "bank"}})
42
43
# Changeset automatically closed when leaving context
44
print("Changeset has been closed")
45
```
46
47
### Changeset Retrieval
48
49
Get changeset information with optional discussion data.
50
51
```python { .api }
52
def ChangesetGet(ChangesetId, include_discussion=False):
53
"""
54
Returns changeset with ChangesetId as a dict.
55
56
Parameters:
57
- ChangesetId (int): Unique identifier of the changeset
58
- include_discussion (bool, optional): Include discussion thread
59
60
Returns:
61
dict: Changeset data including id, open status, tag, timestamps,
62
bounds, user info, and optionally discussion
63
"""
64
```
65
66
**Usage Example:**
67
68
```python
69
import osmapi
70
71
api = osmapi.OsmApi()
72
73
# Get basic changeset info
74
changeset = api.ChangesetGet(12345)
75
print(f"Changeset {changeset['id']}: {changeset['tag'].get('comment', 'No comment')}")
76
print(f"User: {changeset['user']} (ID: {changeset['uid']})")
77
print(f"Created: {changeset['created_at']}")
78
print(f"Open: {changeset['open']}")
79
80
if not changeset['open']:
81
print(f"Closed: {changeset['closed_at']}")
82
83
# Get changeset with discussion
84
changeset_with_discussion = api.ChangesetGet(12345, include_discussion=True)
85
if changeset_with_discussion['discussion']:
86
print(f"\nDiscussion ({len(changeset_with_discussion['discussion'])} comments):")
87
for comment in changeset_with_discussion['discussion']:
88
print(f" {comment['user']}: {comment['text']}")
89
```
90
91
### Manual Changeset Creation
92
93
Create and manage changesets manually for fine-grained control.
94
95
```python { .api }
96
def ChangesetCreate(ChangesetTags={}):
97
"""
98
Opens a changeset.
99
100
Parameters:
101
- ChangesetTags (dict, optional): Tags to apply to the changeset
102
103
Returns:
104
int: Changeset ID
105
106
Raises:
107
- UsernamePasswordMissingError: If no authentication provided
108
- ChangesetAlreadyOpenError: If changeset already open
109
- OsmApiError: If trying to create test changeset on production
110
"""
111
```
112
113
**Usage Example:**
114
115
```python
116
import osmapi
117
118
api = osmapi.OsmApi(username="your_username", password="your_password")
119
120
# Create changeset manually
121
changeset_id = api.ChangesetCreate({
122
"comment": "Manual data cleanup",
123
"source": "Local knowledge",
124
"created_by": "MyEditingApp/1.0"
125
})
126
127
print(f"Created changeset {changeset_id}")
128
129
try:
130
# Perform operations
131
node = api.NodeCreate({"lat": 47.6, "lon": -122.3, "tag": {"amenity": "shop"}})
132
print(f"Created node {node['id']}")
133
134
finally:
135
# Always close changeset
136
closed_id = api.ChangesetClose()
137
print(f"Closed changeset {closed_id}")
138
```
139
140
### Changeset Updates
141
142
Update changeset tags and metadata.
143
144
```python { .api }
145
def ChangesetUpdate(ChangesetTags={}):
146
"""
147
Updates current changeset with ChangesetTags.
148
149
Parameters:
150
- ChangesetTags (dict): Tags to update on the changeset
151
152
Returns:
153
int: Changeset ID
154
155
Raises:
156
- UsernamePasswordMissingError: If no authentication provided
157
- NoChangesetOpenError: If no changeset is open
158
- ChangesetClosedApiError: If changeset is closed
159
"""
160
```
161
162
**Usage Example:**
163
164
```python
165
import osmapi
166
167
api = osmapi.OsmApi(username="your_username", password="your_password")
168
169
changeset_id = api.ChangesetCreate({"comment": "Work in progress"})
170
171
# Update changeset with more detailed information
172
api.ChangesetUpdate({
173
"comment": "Updated building addresses from field survey",
174
"source": "GPS survey + local knowledge",
175
"survey:date": "2024-01-15"
176
})
177
178
# Continue with operations
179
node = api.NodeCreate({"lat": 47.6, "lon": -122.3, "tag": {"addr:housenumber": "123"}})
180
181
api.ChangesetClose()
182
```
183
184
### Changeset Closure
185
186
Explicitly close an open changeset.
187
188
```python { .api }
189
def ChangesetClose():
190
"""
191
Closes current changeset.
192
193
Returns:
194
int: Changeset ID that was closed
195
196
Raises:
197
- UsernamePasswordMissingError: If no authentication provided
198
- NoChangesetOpenError: If no changeset is open
199
- ChangesetClosedApiError: If changeset already closed
200
"""
201
```
202
203
### Batch Upload Operations
204
205
Upload multiple changes to a changeset in a single operation.
206
207
```python { .api }
208
def ChangesetUpload(ChangesData):
209
"""
210
Upload data with the ChangesData list of change operations.
211
212
Parameters:
213
- ChangesData (list[dict]): List of change operations with
214
type, action, and data keys
215
216
Returns:
217
list[dict]: Updated ChangesData with assigned IDs and versions
218
219
Raises:
220
- UsernamePasswordMissingError: If no authentication provided
221
- ChangesetClosedApiError: If changeset is closed
222
"""
223
```
224
225
**Usage Example:**
226
227
```python
228
import osmapi
229
230
api = osmapi.OsmApi(username="your_username", password="your_password")
231
232
changeset_id = api.ChangesetCreate({"comment": "Batch operations example"})
233
234
# Prepare batch changes
235
changes_data = [
236
{
237
"type": "node",
238
"action": "create",
239
"data": {"lat": 47.6, "lon": -122.3, "tag": {"amenity": "cafe"}}
240
},
241
{
242
"type": "node",
243
"action": "create",
244
"data": {"lat": 47.61, "lon": -122.31, "tag": {"amenity": "bank"}}
245
}
246
]
247
248
# Upload all changes at once
249
result = api.ChangesetUpload(changes_data)
250
251
for change in result:
252
if change["action"] == "create":
253
print(f"Created {change['type']} {change['data']['id']}")
254
255
api.ChangesetClose()
256
```
257
258
### Changeset Download
259
260
Download all changes made in a specific changeset.
261
262
```python { .api }
263
def ChangesetDownload(ChangesetId):
264
"""
265
Download data from changeset ChangesetId.
266
267
Parameters:
268
- ChangesetId (int): Unique identifier of the changeset
269
270
Returns:
271
list[dict]: List of change operations with type, action, and data keys
272
"""
273
```
274
275
**Usage Example:**
276
277
```python
278
import osmapi
279
280
api = osmapi.OsmApi()
281
282
# Download changeset data
283
changes = api.ChangesetDownload(12345)
284
285
print(f"Changeset contains {len(changes)} changes:")
286
287
# Analyze changes by type and action
288
summary = {}
289
for change in changes:
290
key = f"{change['action']} {change['type']}"
291
summary[key] = summary.get(key, 0) + 1
292
293
for change_type, count in summary.items():
294
print(f" {count} {change_type} operations")
295
296
# Show details of first few changes
297
for i, change in enumerate(changes[:3]):
298
element = change['data']
299
print(f"\nChange {i+1}: {change['action']} {change['type']} {element['id']}")
300
if 'tag' in element and element['tag']:
301
print(f" Tags: {element['tag']}")
302
```
303
304
### Changeset Queries
305
306
Search for changesets based on various criteria.
307
308
```python { .api }
309
def ChangesetsGet(
310
min_lon=None,
311
min_lat=None,
312
max_lon=None,
313
max_lat=None,
314
userid=None,
315
username=None,
316
closed_after=None,
317
created_before=None,
318
only_open=False,
319
only_closed=False
320
):
321
"""
322
Returns changesets matching criteria as a dict.
323
324
Parameters:
325
- min_lon, min_lat, max_lon, max_lat (float, optional): Bounding box
326
- userid (int, optional): User ID filter
327
- username (str, optional): Username filter
328
- closed_after (str, optional): ISO timestamp filter
329
- created_before (str, optional): ISO timestamp filter
330
- only_open (bool, optional): Only open changesets
331
- only_closed (bool, optional): Only closed changesets
332
333
Returns:
334
dict: Changeset IDs as keys with ChangesetData dicts as values
335
"""
336
```
337
338
**Usage Example:**
339
340
```python
341
import osmapi
342
343
api = osmapi.OsmApi()
344
345
# Find changesets in a bounding box
346
changesets = api.ChangesetsGet(
347
min_lon=-122.4,
348
min_lat=47.5,
349
max_lon=-122.2,
350
max_lat=47.7
351
)
352
353
print(f"Found {len(changesets)} changesets in the area:")
354
for cs_id, cs_data in changesets.items():
355
print(f" {cs_id}: {cs_data['tag'].get('comment', 'No comment')}")
356
print(f" by {cs_data['user']} at {cs_data['created_at']}")
357
358
# Find recent changesets by a specific user
359
recent_changesets = api.ChangesetsGet(
360
username="specific_user",
361
closed_after="2024-01-01T00:00:00Z"
362
)
363
364
print(f"\nRecent changesets by user: {len(recent_changesets)}")
365
```
366
367
### Changeset Discussion
368
369
Add comments and manage discussions on changesets.
370
371
```python { .api }
372
def ChangesetComment(ChangesetId, comment):
373
"""
374
Adds a comment to the changeset ChangesetId.
375
376
Parameters:
377
- ChangesetId (int): Changeset to comment on
378
- comment (str): Comment text
379
380
Returns:
381
dict: Updated ChangesetData with new comment
382
383
Raises:
384
- UsernamePasswordMissingError: If no authentication provided
385
- ChangesetClosedApiError: If changeset is closed
386
"""
387
388
def ChangesetSubscribe(ChangesetId):
389
"""
390
Subscribe to changeset discussion to receive notifications.
391
392
Parameters:
393
- ChangesetId (int): Changeset to subscribe to
394
395
Returns:
396
dict: Updated ChangesetData
397
398
Raises:
399
- UsernamePasswordMissingError: If no authentication provided
400
- AlreadySubscribedApiError: If already subscribed
401
"""
402
403
def ChangesetUnsubscribe(ChangesetId):
404
"""
405
Unsubscribe from changeset discussion notifications.
406
407
Parameters:
408
- ChangesetId (int): Changeset to unsubscribe from
409
410
Returns:
411
dict: Updated ChangesetData
412
413
Raises:
414
- UsernamePasswordMissingError: If no authentication provided
415
- NotSubscribedApiError: If not subscribed
416
"""
417
```
418
419
**Usage Example:**
420
421
```python
422
import osmapi
423
424
api = osmapi.OsmApi(username="your_username", password="your_password")
425
426
changeset_id = 12345
427
428
# Subscribe to discussion
429
try:
430
api.ChangesetSubscribe(changeset_id)
431
print(f"Subscribed to changeset {changeset_id} discussion")
432
except osmapi.AlreadySubscribedApiError:
433
print("Already subscribed to this changeset")
434
435
# Add a comment
436
updated_changeset = api.ChangesetComment(
437
changeset_id,
438
"Thanks for the improvements to the road network!"
439
)
440
441
print(f"Added comment to changeset {changeset_id}")
442
print(f"Comments count: {updated_changeset['comments_count']}")
443
444
# Later, unsubscribe if needed
445
try:
446
api.ChangesetUnsubscribe(changeset_id)
447
print("Unsubscribed from changeset discussion")
448
except osmapi.NotSubscribedApiError:
449
print("Was not subscribed to this changeset")
450
```
451
452
### Automatic Changeset Management
453
454
Force flush of pending changes in automatic mode.
455
456
```python { .api }
457
def flush():
458
"""
459
Force the changes to be uploaded to OSM and the changeset to be closed.
460
461
Raises:
462
- UsernamePasswordMissingError: If no authentication provided
463
- NoChangesetOpenError: If no changeset is open
464
- ChangesetAlreadyOpenError: If changeset conflict occurs
465
"""
466
```
467
468
**Usage Example:**
469
470
```python
471
import osmapi
472
473
# Configure automatic changeset management
474
api = osmapi.OsmApi(
475
username="your_username",
476
password="your_password",
477
changesetauto=True,
478
changesetautotags={"comment": "Automated cleanup"},
479
changesetautosize=100
480
)
481
482
# Make several changes - handled automatically
483
api.NodeCreate({"lat": 47.6, "lon": -122.3, "tag": {"amenity": "cafe"}})
484
api.NodeCreate({"lat": 47.61, "lon": -122.31, "tag": {"amenity": "bank"}})
485
486
# Force immediate upload and closure
487
api.flush()
488
print("All pending changes uploaded and changeset closed")
489
```
490
491
## Changeset Data Structure
492
493
### ChangesetData Dictionary
494
495
```python { .api }
496
ChangesetData = {
497
'id': int, # Changeset ID
498
'open': bool, # True if changeset is still open
499
'tag': dict, # Changeset tags (comment, source, etc.)
500
'created_at': str, # ISO timestamp of creation
501
'closed_at': str, # ISO timestamp of closure (if closed)
502
'comments_count': int, # Number of discussion comments
503
'discussion': list, # Discussion comments (if requested)
504
'max_lon': float, # Eastern boundary of changes
505
'max_lat': float, # Northern boundary of changes
506
'min_lon': float, # Western boundary of changes
507
'min_lat': float, # Southern boundary of changes
508
'user': str, # Username of changeset creator
509
'uid': int # User ID of changeset creator
510
}
511
512
ChangeData = {
513
'type': str, # 'node', 'way', or 'relation'
514
'action': str, # 'create', 'modify', or 'delete'
515
'data': dict # Element data (NodeData, WayData, or RelationData)
516
}
517
```
518
519
## Common Changeset Tags
520
521
### Standard Tags
522
523
```python
524
{
525
"comment": "Updated building addresses from survey",
526
"source": "GPS survey + local knowledge",
527
"created_by": "MyApp/1.0",
528
"locale": "en_US"
529
}
530
```
531
532
### Import Tags
533
534
```python
535
{
536
"comment": "Import government building dataset",
537
"source": "City Planning Department Open Data",
538
"import": "yes",
539
"url": "https://data.city.gov/buildings"
540
}
541
```
542
543
### Mechanical Edit Tags
544
545
```python
546
{
547
"comment": "Fix address format inconsistencies",
548
"source": "existing OSM data",
549
"mechanical": "yes",
550
"bot": "no"
551
}
552
```
553
554
## Changeset Best Practices
555
556
### Good Changeset Comments
557
- Be specific about what was changed
558
- Mention the source of information
559
- Use consistent language and format
560
- Keep comments concise but descriptive
561
562
### Changeset Size Management
563
- Keep changesets focused on related changes
564
- Avoid mixing different types of edits
565
- Use reasonable batch sizes (default 500 elements)
566
- Consider geographic locality of changes
567
568
### Error Handling
569
570
```python
571
import osmapi
572
573
api = osmapi.OsmApi(username="user", password="pass")
574
575
try:
576
changeset_id = api.ChangesetCreate({"comment": "Test changeset"})
577
# ... perform operations
578
api.ChangesetClose()
579
580
except osmapi.ChangesetAlreadyOpenError:
581
print("Already have an open changeset")
582
except osmapi.ChangesetClosedApiError:
583
print("Changeset was closed by someone else or timeout")
584
except osmapi.UsernamePasswordMissingError:
585
print("Authentication required for changeset operations")
586
```