Coverage for brodata / bhr.py: 81%

360 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-20 14:37 +0000

1# ruff: noqa: F401 

2import logging 

3import tempfile 

4 

5import pandas as pd 

6import requests 

7 

8from . import bro 

9 

10logger = logging.getLogger(__name__) 

11 

12 

13class _BoreholeResearch(bro.FileOrUrl): 

14 """Class to represent a Borehole Research (BHR) from the BRO. 

15 

16 BHR-GT: https://www.bro-productomgeving.nl/__attachments/1607501782/DO_ResponseBHR_GT_O_DP.xml?inst-v=b22a9e44-fb13-4fb2-8480-df44d20adeb7 

17 link found at https://www.bro-productomgeving.nl/bpo/latest/bhr-gt-voorbeeldberichten-uitgifte) 

18 """ 

19 

20 def _read_contents(self, tree): 

21 ns = { 

22 "brocom": "http://www.broservices.nl/xsd/brocommon/3.0", 

23 "gml": "http://www.opengis.net/gml/3.2", 

24 "bhrgtcom": "http://www.broservices.nl/xsd/bhrgtcommon/2.1", 

25 "xmlns": self._xmlns, 

26 } 

27 bhr = self._get_main_object(tree, self._object_name, ns) 

28 for key in bhr.attrib: 

29 setattr(self, key.split("}", 1)[1], bhr.attrib[key]) 

30 for child in bhr: 

31 key = self._get_tag(child) 

32 if len(child) == 0: 

33 setattr(self, key, child.text) 

34 elif key == "standardizedLocation": 

35 self._read_standardized_location(child) 

36 elif key == "deliveredLocation": 

37 self._read_delivered_location(child) 

38 elif key in ["researchReportDate"]: 

39 setattr(self, key, self._read_date(child)) 

40 elif key in ["siteCharacteristic"]: 

41 for grandchild in child: 

42 key = self._get_tag(grandchild) 

43 if key in [ 

44 "landUse", 

45 "drained", 

46 "soilUse", 

47 "positionOnGroundBody", 

48 "temporaryChange", 

49 ]: 

50 setattr(self, key, grandchild.text) 

51 elif key in [ 

52 "meanHighestGroundwaterTable", 

53 "meanLowestGroundwaterTable", 

54 ]: 

55 setattr(self, key, float(grandchild.text)) 

56 else: 

57 self._warn_unknown_tag(key) 

58 elif key in ["registrationHistory", "reportHistory"]: 

59 self._read_children_of_children(child) 

60 elif key == "deliveredVerticalPosition": 

61 self._read_delivered_vertical_position(child) 

62 elif key == "boring": 

63 if self._check_single_child_with_tag(child, "Boring"): 

64 child = child[0] 

65 self._read_boring(child) 

66 elif key == "boreholeSampleDescription": 

67 self._read_borehole_sample_description(child) 

68 elif key == "boreholeSampleAnalysis": 

69 # Check if there's an BoreholeSampleAnalysis wrapper element 

70 if self._check_single_child_with_tag(child, "BoreholeSampleAnalysis"): 

71 child = child[0] 

72 self._read_borehole_sample_analysis(child) 

73 else: 

74 self._warn_unknown_tag(key) 

75 for key in [ 

76 "completedInterval", 

77 "boredInterval", 

78 "sampledInterval", 

79 "investigatedInterval", 

80 "boringVelocity", 

81 "excavatedLayer", 

82 ]: 

83 if hasattr(self, key): 

84 setattr(self, key, pd.DataFrame(getattr(self, key))) 

85 

86 def _read_boring(self, node): 

87 for child in node: 

88 key = self._get_tag(child) 

89 if key in ["boringStartDate", "boringEndDate"]: 

90 setattr(self, key, self._read_date(child)) 

91 elif key in [ 

92 "preparation", 

93 "trajectoryExcavated", 

94 "rockReached", 

95 "boringProcedure", 

96 "casingUsed", 

97 "flushingMedium", 

98 "stopCriterion", 

99 "trajectoryRemoved", 

100 "samplingProcedure", 

101 "subsurfaceContaminated", 

102 "boreholeCompleted", 

103 ]: 

104 setattr(self, key, child.text) 

105 elif key in [ 

106 "finalDepthBoring", 

107 "groundwaterLevel", 

108 "finalDepthSampling", 

109 "finalDepthExcavation", 

110 "finalDepthTemporaryCasing", 

111 ]: 

112 setattr(self, key, self._parse_float(child)) 

113 elif key in [ 

114 "temporaryCasingUsed", 

115 "flushingMediumUsed", 

116 "flushingAdditive", 

117 ]: 

118 setattr(self, key, child.text) 

119 elif key == "boredTrajectory": 

120 for grandchild in child: 

121 key = self._get_tag(grandchild) 

122 if key in ["beginDepth", "endDepth"]: 

123 setattr(self, key, self._parse_float(grandchild)) 

124 elif key in ["boredInterval", "sampledInterval", "completedInterval"]: 

125 self._read_interval(child, key) 

126 elif key == "boringTool": 

127 self._read_boring_tool(child) 

128 elif key == "boringVelocity": 

129 if not hasattr(self, key): 

130 setattr(self, key, []) 

131 bv = {} 

132 for grandchild in child: 

133 key2 = self._get_tag(grandchild) 

134 if key2 in ["elapsedTime", "depth"]: 

135 bv[key2] = self._parse_float(grandchild) 

136 else: 

137 self._warn_unknown_tag(key2) 

138 self.boringVelocity.append(bv) 

139 elif key == "excavatedLayer": 

140 if not hasattr(self, key): 

141 setattr(self, key, []) 

142 el = {} 

143 for grandchild in child: 

144 key2 = self._get_tag(grandchild) 

145 if key2 in ["upperBoundary", "lowerBoundary"]: 

146 el[key2] = self._parse_float(grandchild) 

147 elif key2 in ["excavatedMaterial"]: 

148 el[key2] = grandchild.text 

149 else: 

150 self._warn_unknown_tag(key2) 

151 self.excavatedLayer.append(el) 

152 else: 

153 self._warn_unknown_tag(key) 

154 

155 def _read_borehole_sample_analysis(self, node): 

156 for child in node: 

157 key = self._get_tag(child) 

158 if key == "analysisReportDate": 

159 setattr(self, key, self._read_date(child)) 

160 elif key in ["analysisType", "locationSpecific", "analysisProcedure"]: 

161 setattr(self, key, child.text) 

162 elif key == "investigatedInterval": 

163 if not hasattr(self, key): 

164 setattr(self, key, []) 

165 # Check if there's an InvestigatedInterval wrapper element 

166 if self._check_single_child_with_tag(child, "InvestigatedInterval"): 

167 child = child[0] 

168 ii = self._read_investigated_interval(child) 

169 self.investigatedInterval.append(ii) 

170 else: 

171 self._warn_unknown_tag(key) 

172 

173 def _read_investigated_interval(self, node): 

174 d = {} 

175 for child in node: 

176 key = self._get_tag(child) 

177 if key in ["beginDepth", "endDepth"]: 

178 d[key] = self._parse_float(child) 

179 elif key in [ 

180 "locationSpecific", 

181 "sampleQuality", 

182 "analysisType", 

183 "waterContentDetermined", 

184 "organicMatterContentDetermined", 

185 "carbonateContentDetermined", 

186 "volumetricMassDensityDetermined", 

187 "volumetricMassDensitySolidsDetermined", 

188 "described", 

189 ]: 

190 d[key] = child.text 

191 elif key == "particleSizeDistributionDetermination": 

192 if key not in d: 

193 d[key] = [] 

194 for grandchild in child: 

195 key2 = self._get_tag(grandchild) 

196 if key2 == "ParticleSizeDistributionDetermination": 

197 psdd = self._read_particle_size_distribution_determination( 

198 grandchild 

199 ) 

200 d[key].append(psdd) 

201 else: 

202 self._warn_unknown_tag(key2) 

203 elif key == "consistencyLimitsDetermination": 

204 if key not in d: 

205 d[key] = [] 

206 for grandchild in child: 

207 key2 = self._get_tag(grandchild) 

208 if key2 == "ConsistencyLimitsDetermination": 

209 cld = self._read_consistency_limits_determination(grandchild) 

210 d[key].append(cld) 

211 else: 

212 self._warn_unknown_tag(key2) 

213 elif key == "waterContentDetermination": 

214 d[key] = self._read_water_content_determination(child) 

215 elif key == "volumetricMassDensityDetermination": 

216 d[key] = self._read_volumetric_mass_density_determination(child) 

217 elif key == "volumetricMassDensitySolidsDetermination": 

218 d[key] = self._read_volumetric_mass_density_solids_determination(child) 

219 else: 

220 self._warn_unknown_tag(key) 

221 if "particleSizeDistributionDetermination" in d: 

222 d["particleSizeDistributionDetermination"] = pd.DataFrame( 

223 d["particleSizeDistributionDetermination"] 

224 ) 

225 if "consistencyLimitsDetermination" in d: 

226 d["consistencyLimitsDetermination"] = pd.DataFrame( 

227 d["consistencyLimitsDetermination"] 

228 ) 

229 return d 

230 

231 def _read_water_content_determination(self, node): 

232 d = {} 

233 for child in node: 

234 key = self._get_tag(child) 

235 if key in [ 

236 "determinationProcedure", 

237 "determinationMethod", 

238 "sampleMoistness", 

239 "removedMaterial", 

240 ]: 

241 d[key] = child.text 

242 elif key == "determinationResult": 

243 for grandchild in child: 

244 key = self._get_tag(grandchild) 

245 if key in ["waterContent"]: 

246 d[key] = self._parse_float(grandchild) 

247 elif key in [ 

248 "dryingTemperature", 

249 "dryingPeriod", 

250 "saltCorrectionMethod", 

251 ]: 

252 d[key] = grandchild.text 

253 else: 

254 self._warn_unknown_tag(key) 

255 d[key] = self._parse_float(child) 

256 else: 

257 self._warn_unknown_tag(key) 

258 return d 

259 

260 def _read_volumetric_mass_density_determination(self, node): 

261 d = {} 

262 for child in node: 

263 key = self._get_tag(child) 

264 if key in [ 

265 "determinationProcedure", 

266 "determinationMethod", 

267 "sampleMoistness", 

268 ]: 

269 d[key] = child.text 

270 elif key in ["volumetricMassDensity"]: 

271 d[key] = self._parse_float(child) 

272 

273 def _read_volumetric_mass_density_solids_determination(self, node): 

274 d = {} 

275 for child in node: 

276 key = self._get_tag(child) 

277 if key in [ 

278 "determinationProcedure", 

279 "determinationMethod", 

280 "usedMedium", 

281 "sampleContainerVolume", 

282 "removedMaterial", 

283 ]: 

284 d[key] = child.text 

285 elif key in ["volumetricMassDensitySolids"]: 

286 d[key] = self._parse_float(child) 

287 

288 def _read_particle_size_distribution_determination(self, node): 

289 d = {} 

290 for child in node: 

291 key = self._get_tag(child) 

292 if key in [ 

293 "determinationProcedure", 

294 "determinationMethod", 

295 "particleSizeDistributionStandardised", 

296 "dispersionMethod", 

297 ]: 

298 d[key] = child.text 

299 elif key == "nonStandardisedFraction": 

300 if key not in d: 

301 d[key] = [] 

302 d2 = {} 

303 for grandchild in child: 

304 key2 = self._get_tag(grandchild) 

305 if key2 in ["lowerBoundary", "upperBoundary"]: 

306 d2[key2] = int(grandchild.text) 

307 elif key2 in ["proportion"]: 

308 d2[key2] = self._parse_float(grandchild) 

309 else: 

310 self._warn_unknown_tag(key2) 

311 d[key].append(d2) 

312 else: 

313 self._warn_unknown_tag(key) 

314 if "nonStandardisedFraction" in d: 

315 d["nonStandardisedFraction"] = pd.DataFrame(d["nonStandardisedFraction"]) 

316 return d 

317 

318 def _read_consistency_limits_determination(self, node): 

319 d = {} 

320 for child in node: 

321 key = self._get_tag(child) 

322 if key in [ 

323 "determinationProcedure", 

324 "determinationMethod", 

325 "usedMedium", 

326 "conusType", 

327 ]: 

328 d[key] = child.text 

329 elif key in [ 

330 "fractionLarger500um", 

331 "liquidLimit", 

332 "plasticLimit", 

333 "plasticityIndex", 

334 ]: 

335 d[key] = self._parse_float(child) 

336 elif key == "performanceIrregularity": 

337 if key not in d: 

338 d[key] = [] 

339 d[key].append(child.text) 

340 elif key == "plasticityAtSpecificWaterContent": 

341 if key not in d: 

342 d[key] = [] 

343 d2 = {} 

344 for grandchild in child: 

345 key2 = self._get_tag(grandchild) 

346 if key2 in ["waterContent", "penetrationDepth"]: 

347 d2[key2] = self._parse_float(grandchild) 

348 

349 else: 

350 self._warn_unknown_tag(key) 

351 if "plasticityAtSpecificWaterContent" in d: 

352 d["plasticityAtSpecificWaterContent"] = pd.DataFrame( 

353 d["plasticityAtSpecificWaterContent"] 

354 ) 

355 return d 

356 

357 def _read_interval(self, node, key): 

358 if not hasattr(self, key): 

359 setattr(self, key, []) 

360 d = {} 

361 to_float = ["beginDepth", "endDepth"] 

362 self._read_children_of_children(node, d, to_float=to_float) 

363 getattr(self, key).append(d) 

364 

365 def _read_boring_tool(self, node): 

366 d = {} 

367 to_float = ["boringToolDiameter", "beginDepth", "endDepth"] 

368 self._read_children_of_children(node, d, to_float=to_float) 

369 self.boringTool = d 

370 

371 def _read_borehole_sample_description(self, node): 

372 for child in node: 

373 key = self._get_tag(child) 

374 if key == "BoreholeSampleDescription": 

375 self._read_borehole_sample_description(child) 

376 elif key == "descriptiveBoreholeLog": 

377 if not hasattr(self, "descriptiveBoreholeLog"): 

378 self.descriptiveBoreholeLog = [] 

379 if self._check_single_child_with_tag(child, "DescriptiveBoreholeLog"): 

380 child = child[0] 

381 dbl = self._read_descriptive_borehole_log(child) 

382 self.descriptiveBoreholeLog.append(dbl) 

383 elif key == "descriptionReportDate": 

384 setattr(self, key, self._read_date(child)) 

385 elif key == "result": 

386 self._read_borehole_sample_description_result(child) 

387 elif key in ["phenomenonTime", "resultTime"]: 

388 setattr(self, key, self._read_time_instant(child)) 

389 elif key in [ 

390 "descriptionProcedure", 

391 "procedure", 

392 "observedProperty", 

393 "featureOfInterest", 

394 "descriptionMethod", 

395 "descriptionLocation", 

396 "fractionDistributionDetermined", 

397 ]: 

398 setattr(self, key, child.text) 

399 elif key in ["lowerBoundarySandFraction"]: 

400 setattr(self, key, int(child.text)) 

401 elif key == "soilClassification": 

402 if hasattr(self, key): 

403 self._raise_assumed_single(key) 

404 setattr(self, key, self._read_soil_classification(child)) 

405 else: 

406 self._warn_unknown_tag(key) 

407 

408 def _read_soil_classification(self, node): 

409 d = {} 

410 for child in node: 

411 key = self._get_tag(child) 

412 if key in [ 

413 "codeGroup", 

414 "classificationCode", 

415 "featureTop", 

416 "soilClass", 

417 "subsoilPeat", 

418 "lowerBoundaryPeat", 

419 "textureClass", 

420 "textureProfile", 

421 "subsoilDuinVagueSoil", 

422 "carbonateProfile", 

423 "peatClass", 

424 "reworkingClass", 

425 "groundwaterTableClass", 

426 "featureSite", 

427 ]: 

428 d[key] = child.text 

429 elif key == "featureBottom": 

430 for grandchild in child: 

431 key = self._get_tag(grandchild) 

432 if key == "feature": 

433 d[key] = grandchild.text 

434 elif key == "beginDepth": 

435 d[key] = float(grandchild.text) 

436 else: 

437 self._warn_unknown_tag(key) 

438 else: 

439 self._warn_unknown_tag(key) 

440 return d 

441 

442 def _read_borehole_sample_description_result(self, node): 

443 boreholeSampleDescription = [] 

444 for child in node: 

445 key = self._get_tag(child) 

446 if len(child) == 0: 

447 setattr(self, key, child.text) 

448 elif key == "soilLayer": 

449 d = {} 

450 to_float = [ 

451 "upperBoundary", 

452 "lowerBoundary", 

453 "organicMatterContent", 

454 "clayContent", 

455 ] 

456 to_int = ["numberOfLayerComponents"] 

457 self._read_children_of_children( 

458 child, d=d, to_float=to_float, to_int=to_int 

459 ) 

460 boreholeSampleDescription.append(d) 

461 elif key == "litterLayer": 

462 if hasattr(self, key): 

463 self._raise_assumed_single(key) 

464 setattr(self, key, {}) 

465 for grandchild in child: 

466 key = self._get_tag(grandchild) 

467 if key in [ 

468 "upperBoundary", 

469 "lowerBoundary", 

470 "organicMatterContent", 

471 ]: 

472 self.litterLayer[key] = float(grandchild.text) 

473 elif key in ["horizonCode", "litterType"]: 

474 self.litterLayer[key] = grandchild.text 

475 else: 

476 self._warn_unknown_tag(key) 

477 

478 else: 

479 self._warn_unknown_tag(key) 

480 df = pd.DataFrame(boreholeSampleDescription) 

481 if hasattr(self, "boreholeSampleDescription"): 

482 self._raise_assumed_single("boreholeSampleDescription") 

483 setattr(self, "boreholeSampleDescription", df) 

484 

485 

486class GeotechnicalBoreholeResearch(_BoreholeResearch): 

487 """Class to represent a Geotechnical Borehole Research (BHR_GT) from the BRO.""" 

488 

489 _object_name = "BHR_GT_O" 

490 _xmlns = "http://www.broservices.nl/xsd/dsbhr-gt/2.1" 

491 _rest_url = "https://publiek.broservices.nl/sr/bhrgt/v2" 

492 _char = "BHR_GT_C" 

493 

494 

495def bhrgt_graph( 

496 xml_file, 

497 to_file=None, 

498 timeout=5, 

499 language="nl", 

500 asNAP=False, 

501 topMv=None, 

502 bottomMv=None, 

503 topNap=None, 

504 bottomNap=None, 

505 return_fname=False, 

506): 

507 """ 

508 Generate a svg-graph of a bhrgt-file (GeotechnicalBoreholeResearch). 

509 

510 Parameters 

511 ---------- 

512 xml_file : str 

513 The filename of the xml-file to generate a graphical representation of. 

514 to_file : str, optional 

515 The filename to save the svg-file to. The default is None. 

516 timeout : int or float, optional 

517 A number indicating how many seconds to wait for the client to make a connection 

518 and/or send a response. The default is 5. 

519 language : str, optional 

520 DESCRIPTION. The default is "nl". 

521 asNAP : bool, optional 

522 If True, display the height of the drilling in m NAP. If False, display the 

523 height in meter below surface level. The default is False. 

524 topMv : float, optional 

525 Highest point in the graph, in m below surface level (mv). Needs to be specified 

526 together with bottomMv. The default is None. 

527 bottomMv : float, optional 

528 Lowest point in the graph, in m below surface level (mv). Needs to be specified 

529 together with topMv. The default is None. 

530 topNap : float, optional 

531 Highest point in the graph, in m NAP. Needs to be specified together with 

532 bottomNap. The default is None. 

533 bottomNap : float, optional 

534 Lowest point in the graph, in m NAP. Needs to be specified together with 

535 topNAP. The default is None. 

536 return_fname : bool, optional 

537 If True, Return the filename of the svg-file. The default is False. 

538 

539 Returns 

540 ------- 

541 IPython.display.SVG or str 

542 A graphical representation of the svg-file or the filename of the svg-file. 

543 

544 """ 

545 url = "https://publiek.broservices.nl/sr/bhrgt/v2/profile/graph/dispatch" 

546 params = {"language": language, "asNAP": asNAP} 

547 

548 if ((topMv is None) + (bottomMv is None)) == 1: 

549 raise (ValueError("Both topMv and bottomMv need to be specified, or none")) 

550 if topMv is not None: 

551 params["topMv"] = topMv 

552 params["bottomMv"] = bottomMv 

553 

554 if ((topNap is None) + (bottomNap is None)) == 1: 

555 raise (ValueError("Both topNap and bottomNap need to be specified, or none")) 

556 if topNap is not None: 

557 params["topNap"] = topNap 

558 params["bottomNap"] = bottomNap 

559 

560 with open(xml_file, "rb") as data: 

561 r = requests.post(url, data=data, timeout=timeout, params=params) 

562 r.raise_for_status() 

563 if to_file is None: 

564 to_file = tempfile.NamedTemporaryFile(suffix=".svg").name 

565 with open(to_file, "w", encoding="utf-8") as f: 

566 f.write(r.text) 

567 if return_fname: 

568 return to_file 

569 else: 

570 from IPython.display import SVG 

571 

572 return SVG(to_file) 

573 

574 

575class PedologicalBoreholeResearch(_BoreholeResearch): 

576 """Class to represent a Pedological Borehole Research (BHR_P) from the BRO.""" 

577 

578 _object_name = "BHR_O" 

579 _xmlns = "http://www.broservices.nl/xsd/dsbhr/2.0" 

580 _rest_url = "https://publiek.broservices.nl/sr/bhrp/v2" 

581 _char = "BHR_C" 

582 

583 

584class GeologicalBoreholeResearch(_BoreholeResearch): 

585 """Class to represent a Geological Borehole Research (BHR_G) from the BRO.""" 

586 

587 _object_name = "BHR_G_O" 

588 _xmlns = "http://www.broservices.nl/xsd/dsbhrg/3.1" 

589 _rest_url = "https://publiek.broservices.nl/sr/bhrg/v3" 

590 _char = "BHR_C" 

591 

592 

593def get_bro_ids_of_bronhouder(bronhouder, bhr_class=GeotechnicalBoreholeResearch): 

594 """ 

595 Retrieve list of BRO (Basisregistratie Ondergrond) IDs for a given bronhouder. 

596 

597 This function sends a GET request to the REST API to fetch the BRO IDs associated 

598 with the specified bronhouder. If the request is unsuccessful, it logs an error 

599 message. 

600 

601 Parameters 

602 ---------- 

603 bronhouder : str 

604 The identifier for the bronhouder to retrieve the associated BRO IDs. 

605 bhr_class : class 

606 The class of borehole objects. The default is GeotechnicalBoreholeResearch. 

607 Other options are PedologicalBoreholeResearch and GeologicalBoreholeResearch. 

608 

609 Returns 

610 ------- 

611 list or None 

612 A list of BRO IDs if the request is successful. Returns `None` if the request 

613 fails. 

614 """ 

615 return bro._get_bro_ids_of_bronhouder(bhr_class, bronhouder) 

616 

617 

618def get_data_for_bro_ids(bhr_class=GeotechnicalBoreholeResearch, **kwargs): 

619 _check_first_argument(bhr_class) 

620 return bro._get_data_for_bro_ids(bhr_class, **kwargs) 

621 

622 

623def get_characteristics(bhr_class=GeotechnicalBoreholeResearch, **kwargs): 

624 """ 

625 Get characteristics of a set of registered objects for a given object class. 

626 

627 The maximum number of objects that can be retrieved is 2000 for a single request. 

628 

629 Parameters 

630 ---------- 

631 bhr_class : class 

632 The class of borehole objects. The default is GeotechnicalBoreholeResearch. 

633 Other options are PedologicalBoreholeResearch and GeologicalBoreholeResearch. 

634 tmin : str or pd.Timestamp, optional 

635 The minimum registrationPeriod of the requested characteristics. The default is 

636 None. 

637 tmax : str or pd.Timestamp, optional 

638 The maximum registrationPeriod of the requested characteristics. The default is 

639 None. 

640 extent : list or tuple of 4 floats, optional 

641 Download the characteristics within extent ([xmin, xmax, ymin, ymax]). The 

642 default is None. 

643 x : float, optional 

644 The x-coordinate of the center of the circle in which the characteristics are 

645 requested. The default is None. 

646 y : float, optional 

647 The y-coordinate of the center of the circle in which the characteristics are 

648 requested. The default is None. 

649 radius : float, optional 

650 The radius in meters of the center of the circle in which the characteristics 

651 are requested. The default is 1000.0. 

652 epsg : str, optional 

653 The coordinate reference system of the specified extent, x or y, and of the 

654 resulting GeoDataFrame. The default is 28992, which is the Dutch RD-system. 

655 to_file : str, optional 

656 When not None, save the characteristics to a file with a name as specified in 

657 to_file. The defaults None. 

658 use_all_corners_of_extent : bool, optional 

659 Because the extent by default is given in epsg 28992, some locations along the 

660 border of a requested extent will not be returned in the result. To solve this 

661 issue, when use_all_corners_of_extent is True, all four corners of the extent 

662 are used to calculate the minimum and maximum lan and lon values. The default is 

663 True. 

664 timeout : int or float, optional 

665 A number indicating how many seconds to wait for the client to make a connection 

666 and/or send a response. The default is 5. 

667 

668 Returns 

669 ------- 

670 gpd.GeoDataFrame 

671 A GeoDataFrame contraining the characteristics. 

672 

673 Notes 

674 ----- 

675 Haalt de karakteristieken op van een set van registratie objecten, gegeven een 

676 kenmerkenverzameling (kenset). 

677 

678 De karakteristieken geven een samenvatting van een object zodat een verdere selectie 

679 gemaakt kan worden. Het past in een tweetrapsbenadering, waarbij de eerste stap 

680 bestaat uit het ophalen van de karakteristieken en de 2e stap uit het ophalen van de 

681 gewenste registratie objecten. Het resultaat van deze operatie is gemaximaliseerd op 

682 2000. 

683 """ 

684 if not isinstance(bhr_class, type): 

685 raise TypeError( 

686 "First argument must be a class (a subclass of _BoreholeResearch), " 

687 "for example GeotechnicalBoreholeResearch. " 

688 "If you passed other arguments positionally, use keyword arguments " 

689 "instead, e.g. extent=[xmin, xmax, ymin, ymax]." 

690 ) 

691 return bro._get_characteristics(bhr_class, **kwargs) 

692 

693 

694def get_data_in_extent(bhr_class=GeotechnicalBoreholeResearch, **kwargs): 

695 _check_first_argument(bhr_class) 

696 return bro._get_data_in_extent(bhr_class, **kwargs) 

697 

698 

699def _check_first_argument(bhr_class): 

700 if not isinstance(bhr_class, type): 

701 raise TypeError( 

702 "First argument must be a class (a subclass of _BoreholeResearch), " 

703 "for example GeotechnicalBoreholeResearch. " 

704 "If you passed other arguments positionally, use keyword arguments " 

705 "instead, e.g. extent=[xmin, xmax, ymin, ymax]." 

706 )