| 69 |
69 |
|
| 70 |
70 |
/// Univers cartographiables (coordonnées disponibles) |
| 71 |
71 |
private var mapUniverses: [Universe] { |
| 72 |
|
− Ecosystem.all.filter { ["lou-ka", "immo-ka", "job-ka", "resto-ka", "sorti-ka"].contains($0.id) } |
|
72 |
+ Ecosystem.all.filter { ["lou-ka", "immo-ka", "job-ka", "resto-ka", "sorti-ka", "vrai-prix"].contains($0.id) } |
|
73 |
+ } |
|
74 |
+ /// La couche Vrai-Prix (3,7 M d'unités d'évaluation) ne se charge |
|
75 |
+ /// qu'assez zoomé : au-delà, le serveur plafonne le bbox de /api/nearby |
|
76 |
+ /// (~9 km) et la zone visible ne serait couverte qu'au centre. |
|
77 |
+ private var vpZoomedEnough: Bool { |
|
78 |
+ visibleRegion.span.latitudeDelta <= 0.16 && visibleRegion.span.longitudeDelta <= 0.24 |
| 73 |
79 |
} |
| 74 |
80 |
private var visibleItems: [KAItem] { items.filter { enabled.contains($0.universeID) } } |
| 75 |
81 |
private var clusters: [MapCluster] { ClusterEngine.clusterize(visibleItems, region: visibleRegion) } |
| 106 |
112 |
} |
| 107 |
113 |
} |
| 108 |
114 |
.sheet(item: $sheetItem) { item in |
| 109 |
|
− NavigationStack { ItemDetailView(item: item) } |
| 110 |
|
− .presentationDetents([.medium, .large]) |
| 111 |
|
− .presentationBackgroundInteraction(.enabled(upThrough: .medium)) |
|
115 |
+ NavigationStack { |
|
116 |
+ // une unité Vrai-Prix ouvre la vraie fiche d'estimation |
|
117 |
+ // (moteur hédonique : fourchette, confiance, historique) |
|
118 |
+ if item.universeID == "vrai-prix", |
|
119 |
+ let vp = Ecosystem.universe("vrai-prix") { |
|
120 |
+ EstimateView(result: VPResult(id: String(item.id.dropFirst(3)), |
|
121 |
+ adresse: item.title, |
|
122 |
+ municipalite: item.city ?? "", |
|
123 |
+ type: item.subtitle), |
|
124 |
+ universe: vp) |
|
125 |
+ } else { |
|
126 |
+ ItemDetailView(item: item) |
|
127 |
+ } |
|
128 |
+ } |
|
129 |
+ .presentationDetents([.medium, .large]) |
|
130 |
+ .presentationBackgroundInteraction(.enabled(upThrough: .medium)) |
| 112 |
131 |
} |
| 113 |
132 |
.task { |
|
133 |
+ // pilotage debug : couche Vrai-Prix active + zoom quartier |
|
134 |
+ // (defaults write com.groupeka.ka ka.debug.mapvp -bool true) |
|
135 |
+ if UserDefaults.standard.bool(forKey: "ka.debug.mapvp") { |
|
136 |
+ enabled.insert("vrai-prix") |
|
137 |
+ try? await Task.sleep(for: .seconds(1)) // la carte doit être layoutée |
|
138 |
+ let r = MKCoordinateRegion(center: .init(latitude: 45.5231, longitude: -73.5817), |
|
139 |
+ span: .init(latitudeDelta: 0.005, longitudeDelta: 0.005)) |
|
140 |
+ visibleRegion = r |
|
141 |
+ recenter(r) |
|
142 |
+ } |
| 114 |
143 |
location.requestIfAuthorized() // HIG : pas de dialogue à l'ouverture |
| 115 |
144 |
await loadZone() |
| 116 |
145 |
} |
| 117 |
146 |
.onChange(of: location.coordinate != nil) { |
|
147 |
+ guard !UserDefaults.standard.bool(forKey: "ka.debug.mapvp") else { return } |
| 118 |
148 |
if let c = location.coordinate { |
| 119 |
149 |
recenter(.init(center: c, span: .init(latitudeDelta: 0.18, longitudeDelta: 0.18))) |
| 120 |
150 |
Task { await loadZone() } |
| 217 |
247 |
.font(.system(.caption, design: .monospaced).weight(.bold)) |
| 218 |
248 |
.padding(.horizontal, 11).padding(.vertical, 9) |
| 219 |
249 |
.background(.ultraThinMaterial, in: Capsule()) |
|
250 |
+ if enabled.contains("vrai-prix") && !vpZoomedEnough { |
|
251 |
+ Label("Vrai-Prix : zoomez", systemImage: "plus.magnifyingglass") |
|
252 |
+ .font(.system(size: 10, design: .monospaced).weight(.bold)) |
|
253 |
+ .padding(.horizontal, 10).padding(.vertical, 9) |
|
254 |
+ .background(.ultraThinMaterial, in: Capsule()) |
|
255 |
+ .foregroundStyle(Ecosystem.universe("vrai-prix")?.accent ?? .red) |
|
256 |
+ .transition(.scale.combined(with: .opacity)) |
|
257 |
+ .accessibilityLabel("Zoomez la carte pour voir les estimations Vrai-Prix") |
|
258 |
+ } |
| 220 |
259 |
Spacer() |
| 221 |
260 |
if zoneDirty && !loading { |
| 222 |
261 |
Button { |
| 311 |
350 |
let south = r.center.latitude - r.span.latitudeDelta / 2 |
| 312 |
351 |
let north = r.center.latitude + r.span.latitudeDelta / 2 |
| 313 |
352 |
var all: [KAItem] = [] |
|
353 |
+ let wantVP = enabled.contains("vrai-prix") && vpZoomedEnough |
| 314 |
354 |
await withTaskGroup(of: [KAItem].self) { group in |
| 315 |
|
− for u in mapUniverses where enabled.contains(u.id) { |
|
355 |
+ for u in mapUniverses where enabled.contains(u.id) && u.id != "vrai-prix" { |
| 316 |
356 |
group.addTask { |
| 317 |
357 |
await UniverseService.mapItems(u, west: west, south: south, east: east, north: north) |
| 318 |
358 |
} |
| 319 |
359 |
} |
|
360 |
+ if wantVP { |
|
361 |
+ group.addTask { |
|
362 |
+ await UniverseService.vraiPrixUnits(west: west, south: south, east: east, north: north) |
|
363 |
+ } |
|
364 |
+ } |
| 320 |
365 |
for await batch in group { all.append(contentsOf: batch) } |
| 321 |
366 |
} |
| 322 |
367 |
withAnimation(.easeOut(duration: 0.25)) { items = all } |
| 323 |
368 |
|