or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdchangesets.mderrors.mdindex.mdnodes.mdnotes.mdrelations.mdways.md

relations.mddocs/

0

# Relation Operations

1

2

Comprehensive relation management including creation, modification, deletion, and nested relation handling. Relations group other OSM elements (nodes, ways, and other relations) together to represent complex geographic features like multipolygons, routes, and administrative boundaries.

3

4

## Capabilities

5

6

### Relation Retrieval

7

8

Get individual relations by ID with optional version specification.

9

10

```python { .api }

11

def RelationGet(RelationId, RelationVersion=-1):

12

"""

13

Returns relation with RelationId as a dict.

14

15

Parameters:

16

- RelationId (int): Unique identifier of the relation

17

- RelationVersion (int, optional): Specific version to retrieve (-1 for latest)

18

19

Returns:

20

dict: Relation data including id, member (list), tag, changeset, version,

21

user, uid, timestamp, visible

22

23

Raises:

24

- ElementDeletedApiError: If relation has been deleted

25

- ElementNotFoundApiError: If relation cannot be found

26

"""

27

```

28

29

**Usage Example:**

30

31

```python

32

import osmapi

33

34

api = osmapi.OsmApi()

35

36

# Get latest version of relation

37

relation = api.RelationGet(789)

38

print(f"Relation {relation['id']}: {relation['tag'].get('name', 'Unnamed')}")

39

print(f"Type: {relation['tag'].get('type', 'unknown')}")

40

print(f"Members: {len(relation['member'])}")

41

42

# Show member details

43

for member in relation['member']:

44

print(f" {member['type']} {member['ref']} as '{member['role']}'")

45

46

# Get specific version

47

relation_v2 = api.RelationGet(789, RelationVersion=2)

48

print(f"Version 2 had {len(relation_v2['member'])} members")

49

```

50

51

### Relation Creation

52

53

Create new relations by grouping existing elements with defined roles.

54

55

```python { .api }

56

def RelationCreate(RelationData):

57

"""

58

Creates a relation based on the supplied RelationData dict.

59

60

Parameters:

61

- RelationData (dict): Relation data with member list and optional tag

62

Required: member (list[dict]) - list of member objects

63

Optional: tag (dict)

64

65

Returns:

66

dict: Updated RelationData with assigned id, version, changeset,

67

user, uid, visible

68

69

Raises:

70

- UsernamePasswordMissingError: If no authentication provided

71

- NoChangesetOpenError: If no changeset is open

72

- OsmTypeAlreadyExistsError: If relation data contains existing ID

73

- ChangesetClosedApiError: If changeset is closed

74

- PreconditionFailedApiError: If referenced elements don't exist

75

"""

76

```

77

78

**Usage Example:**

79

80

```python

81

import osmapi

82

83

api = osmapi.OsmApi(username="your_username", password="your_password")

84

85

with api.Changeset({"comment": "Creating bus route relation"}) as changeset_id:

86

# Create a bus route relation

87

new_relation = api.RelationCreate({

88

"member": [

89

{"type": "node", "ref": 12345, "role": "stop"},

90

{"type": "way", "ref": 67890, "role": ""},

91

{"type": "way", "ref": 67891, "role": ""},

92

{"type": "node", "ref": 12346, "role": "stop"},

93

{"type": "way", "ref": 67892, "role": ""},

94

{"type": "node", "ref": 12347, "role": "stop"}

95

],

96

"tag": {

97

"type": "route",

98

"route": "bus",

99

"name": "Bus Route 42",

100

"ref": "42",

101

"operator": "City Transit",

102

"public_transport:version": "2"

103

}

104

})

105

print(f"Created relation {new_relation['id']} with {len(new_relation['member'])} members")

106

```

107

108

### Relation Updates

109

110

Modify existing relations by updating members, tags, or other attributes.

111

112

```python { .api }

113

def RelationUpdate(RelationData):

114

"""

115

Updates relation with the supplied RelationData dict.

116

117

Parameters:

118

- RelationData (dict): Relation data with id, version, and updated fields

119

Required: id (int), member (list[dict]), version (int)

120

Optional: tag (dict)

121

122

Returns:

123

dict: Updated RelationData with new version, changeset, user, uid, visible

124

125

Raises:

126

- UsernamePasswordMissingError: If no authentication provided

127

- NoChangesetOpenError: If no changeset is open

128

- VersionMismatchApiError: If version doesn't match current

129

- ChangesetClosedApiError: If changeset is closed

130

- PreconditionFailedApiError: If referenced elements don't exist

131

"""

132

```

133

134

**Usage Example:**

135

136

```python

137

import osmapi

138

139

api = osmapi.OsmApi(username="your_username", password="your_password")

140

141

# Get current relation data

142

relation = api.RelationGet(12345)

143

144

with api.Changeset({"comment": "Adding stop to bus route"}) as changeset_id:

145

# Add a new stop to the route

146

new_stop = {"type": "node", "ref": 98765, "role": "stop"}

147

relation["member"].append(new_stop)

148

149

# Update route information

150

relation["tag"]["note"] = "Route extended to new terminal"

151

152

updated_relation = api.RelationUpdate(relation)

153

print(f"Updated relation {updated_relation['id']} to version {updated_relation['version']}")

154

print(f"Now has {len(updated_relation['member'])} members")

155

```

156

157

### Relation Deletion

158

159

Delete relations from OpenStreetMap (marks as invisible).

160

161

```python { .api }

162

def RelationDelete(RelationData):

163

"""

164

Delete relation with RelationData.

165

166

Parameters:

167

- RelationData (dict): Relation data with id, version, and current attributes

168

Required: id (int), member (list[dict]), version (int)

169

Optional: tag (dict)

170

171

Returns:

172

dict: Updated RelationData with visible=False and new version

173

174

Raises:

175

- UsernamePasswordMissingError: If no authentication provided

176

- NoChangesetOpenError: If no changeset is open

177

- VersionMismatchApiError: If version doesn't match current

178

- ChangesetClosedApiError: If changeset is closed

179

- ElementDeletedApiError: If relation already deleted

180

- ElementNotFoundApiError: If relation cannot be found

181

- PreconditionFailedApiError: If relation is still used by other relations

182

"""

183

```

184

185

**Usage Example:**

186

187

```python

188

import osmapi

189

190

api = osmapi.OsmApi(username="your_username", password="your_password")

191

192

# Get relation to delete

193

relation = api.RelationGet(12345)

194

195

# Check if relation is used in other relations first

196

parent_relations = api.RelationRelations(relation["id"])

197

if parent_relations:

198

print(f"Warning: Relation {relation['id']} is used in {len(parent_relations)} other relations")

199

200

with api.Changeset({"comment": "Removing obsolete relation"}) as changeset_id:

201

deleted_relation = api.RelationDelete(relation)

202

print(f"Deleted relation {deleted_relation['id']}, now version {deleted_relation['version']}")

203

print(f"Visible: {deleted_relation['visible']}") # False

204

```

205

206

### Relation History

207

208

Retrieve complete version history for a relation.

209

210

```python { .api }

211

def RelationHistory(RelationId):

212

"""

213

Returns dict with version as key containing all relation versions.

214

215

Parameters:

216

- RelationId (int): Unique identifier of the relation

217

218

Returns:

219

dict: Version history with version numbers as keys and

220

RelationData dicts as values

221

"""

222

```

223

224

**Usage Example:**

225

226

```python

227

import osmapi

228

229

api = osmapi.OsmApi()

230

231

# Get complete history

232

history = api.RelationHistory(789)

233

234

for version, relation_data in history.items():

235

print(f"Version {version}: {relation_data['user']} at {relation_data['timestamp']}")

236

print(f" Members: {len(relation_data['member'])}")

237

print(f" Type: {relation_data['tag'].get('type', 'unknown')}")

238

239

# Show member changes

240

member_types = {}

241

for member in relation_data['member']:

242

member_types[member['type']] = member_types.get(member['type'], 0) + 1

243

print(f" Composition: {dict(member_types)}")

244

```

245

246

### Relation Relationships

247

248

Find relations that reference a specific relation.

249

250

```python { .api }

251

def RelationRelations(RelationId):

252

"""

253

Returns a list of relations containing the specified relation.

254

255

Parameters:

256

- RelationId (int): Unique identifier of the relation

257

258

Returns:

259

list[dict]: List of RelationData dicts containing the relation

260

"""

261

```

262

263

**Usage Example:**

264

265

```python

266

import osmapi

267

268

api = osmapi.OsmApi()

269

270

relation_id = 789

271

272

# Find parent relations

273

parent_relations = api.RelationRelations(relation_id)

274

print(f"Relation {relation_id} is used in {len(parent_relations)} other relations:")

275

276

for parent in parent_relations:

277

print(f" Relation {parent['id']}: {parent['tag'].get('name', 'Unnamed')}")

278

print(f" Type: {parent['tag'].get('type', 'unknown')}")

279

280

# Find this relation's role in the parent

281

for member in parent['member']:

282

if member['type'] == 'relation' and member['ref'] == relation_id:

283

print(f" Role: {member['role']}")

284

```

285

286

### Relation with Full Data

287

288

Retrieve relation with all referenced elements at two levels deep.

289

290

```python { .api }

291

def RelationFull(RelationId):

292

"""

293

Returns the full data (two levels) for relation RelationId as list of dicts.

294

295

Parameters:

296

- RelationId (int): Unique identifier of the relation

297

298

Returns:

299

list[dict]: List of elements with type and data keys, including

300

the relation itself and all directly referenced elements

301

302

Raises:

303

- ElementDeletedApiError: If relation has been deleted

304

- ElementNotFoundApiError: If relation cannot be found

305

"""

306

```

307

308

### Recursive Relation Full Data

309

310

Retrieve relation with all referenced elements at all levels (recursive).

311

312

```python { .api }

313

def RelationFullRecur(RelationId):

314

"""

315

Returns the full data (all levels) for relation RelationId as list of dicts.

316

317

Parameters:

318

- RelationId (int): Unique identifier of the relation

319

320

Returns:

321

list[dict]: List of elements with type and data keys, including

322

the relation itself and all recursively referenced elements

323

324

Raises:

325

- ElementDeletedApiError: If any relation has been deleted

326

- ElementNotFoundApiError: If relation cannot be found

327

"""

328

```

329

330

**Usage Example:**

331

332

```python

333

import osmapi

334

335

api = osmapi.OsmApi()

336

337

# Get relation with two levels of data

338

full_data = api.RelationFull(789)

339

340

relation_data = None

341

nodes_data = []

342

ways_data = []

343

child_relations_data = []

344

345

for element in full_data:

346

if element['type'] == 'relation':

347

if element['data']['id'] == 789:

348

relation_data = element['data']

349

else:

350

child_relations_data.append(element['data'])

351

elif element['type'] == 'way':

352

ways_data.append(element['data'])

353

elif element['type'] == 'node':

354

nodes_data.append(element['data'])

355

356

print(f"Relation {relation_data['id']}: {relation_data['tag'].get('name', 'Unnamed')}")

357

print(f"Contains {len(nodes_data)} nodes, {len(ways_data)} ways, {len(child_relations_data)} relations")

358

359

# For complex nested relations, use recursive version

360

if child_relations_data:

361

print("Getting recursive data for complex relation...")

362

recursive_data = api.RelationFullRecur(789)

363

print(f"Recursive data contains {len(recursive_data)} total elements")

364

```

365

366

### Bulk Relation Operations

367

368

Retrieve multiple relations in a single API call for efficiency.

369

370

```python { .api }

371

def RelationsGet(RelationIdList):

372

"""

373

Returns dict with relation IDs as keys for multiple relations.

374

375

Parameters:

376

- RelationIdList (list[int]): List of relation IDs to retrieve

377

378

Returns:

379

dict: Relation IDs as keys with RelationData dicts as values

380

"""

381

```

382

383

**Usage Example:**

384

385

```python

386

import osmapi

387

388

api = osmapi.OsmApi()

389

390

# Get multiple relations efficiently

391

relation_ids = [789, 101112, 131415, 161718]

392

relations = api.RelationsGet(relation_ids)

393

394

for relation_id, relation_data in relations.items():

395

relation_type = relation_data['tag'].get('type', 'unknown')

396

name = relation_data['tag'].get('name', 'Unnamed')

397

print(f"Relation {relation_id}: {relation_type} - {name}")

398

print(f" Members: {len(relation_data['member'])}")

399

400

# Check for missing relations

401

found_ids = set(relations.keys())

402

missing_ids = set(relation_ids) - found_ids

403

if missing_ids:

404

print(f"Missing relations: {missing_ids}")

405

```

406

407

## Relation Data Structure

408

409

### RelationData Dictionary

410

411

```python { .api }

412

RelationData = {

413

'id': int, # Relation ID (assigned by OSM after creation)

414

'member': list[dict], # List of member objects (required)

415

'tag': dict, # Key-value pairs for attributes

416

'version': int, # Version number (starts at 1)

417

'changeset': int, # ID of changeset that last modified this relation

418

'user': str, # Username of last editor

419

'uid': int, # User ID of last editor

420

'timestamp': str, # ISO timestamp of last modification

421

'visible': bool # True if visible, False if deleted

422

}

423

424

RelationMember = {

425

'type': str, # 'node', 'way', or 'relation'

426

'ref': int, # Referenced element ID

427

'role': str # Role description (can be empty string)

428

}

429

```

430

431

## Common Relation Types

432

433

### Multipolygon Relations

434

Complex areas with holes or multiple parts.

435

436

```python

437

multipolygon_relation = {

438

"member": [

439

{"type": "way", "ref": outer_way_id, "role": "outer"},

440

{"type": "way", "ref": inner_way_id, "role": "inner"}

441

],

442

"tag": {

443

"type": "multipolygon",

444

"natural": "forest",

445

"name": "Central Forest Reserve"

446

}

447

}

448

```

449

450

### Route Relations

451

Transportation or hiking routes.

452

453

```python

454

bus_route_relation = {

455

"member": [

456

{"type": "node", "ref": stop1_id, "role": "stop"},

457

{"type": "way", "ref": way1_id, "role": ""},

458

{"type": "node", "ref": stop2_id, "role": "stop"},

459

{"type": "way", "ref": way2_id, "role": ""}

460

],

461

"tag": {

462

"type": "route",

463

"route": "bus",

464

"name": "Bus Line 42",

465

"ref": "42",

466

"operator": "Metro Transit"

467

}

468

}

469

```

470

471

### Boundary Relations

472

Administrative or other boundaries.

473

474

```python

475

boundary_relation = {

476

"member": [

477

{"type": "way", "ref": boundary_way1_id, "role": "outer"},

478

{"type": "way", "ref": boundary_way2_id, "role": "outer"}

479

],

480

"tag": {

481

"type": "boundary",

482

"boundary": "administrative",

483

"admin_level": "6",

484

"name": "Example County"

485

}

486

}

487

```

488

489

## Relation Best Practices

490

491

### Member Ordering

492

- Order members logically (e.g., stops along a route)

493

- For multipolygons, list outer ways first, then inner ways

494

- Maintain consistent member ordering across versions

495

496

### Role Assignment

497

- Use standard roles for relation types

498

- Empty roles are valid and often used

499

- Document custom roles clearly in relation tags

500

501

### Nested Relations

502

- Avoid deeply nested relation hierarchies

503

- Use RelationFullRecur() for complex nested structures

504

- Be careful with circular references

505

506

## Error Handling

507

508

Relation operations can raise various exceptions:

509

510

```python

511

import osmapi

512

513

api = osmapi.OsmApi(username="user", password="pass")

514

515

try:

516

relation = api.RelationGet(999999)

517

except osmapi.ElementNotFoundApiError:

518

print("Relation does not exist")

519

except osmapi.ElementDeletedApiError:

520

print("Relation has been deleted")

521

522

try:

523

with api.Changeset({"comment": "Test"}) as changeset_id:

524

# This will fail if referenced elements don't exist

525

api.RelationCreate({

526

"member": [

527

{"type": "node", "ref": 999999, "role": "stop"} # Non-existent node

528

],

529

"tag": {"type": "route", "route": "bus"}

530

})

531

except osmapi.PreconditionFailedApiError:

532

print("Referenced elements don't exist or are not visible")

533

except osmapi.VersionMismatchApiError:

534

print("Version conflict - relation was modified by another user")

535

```