|
|
@ -118,9 +118,10 @@ |
|
|
|
<div v-if="isMappedActive"> |
|
|
|
<div class="mt-4 h-96 w-5/6 mx-auto"> |
|
|
|
<l-map |
|
|
|
ref="map" |
|
|
|
ref="projectMap" |
|
|
|
:center="[localCenterLat, localCenterLong]" |
|
|
|
:zoom="2" |
|
|
|
@ready="onMapReady" |
|
|
|
@moveend="onMoveEnd" |
|
|
|
@zoomend="onZoomEnd" |
|
|
|
@zoomstart="onZoomStart" |
|
|
@ -147,7 +148,9 @@ |
|
|
|
<span v-if="searchBox"> None found in the selected area. </span> |
|
|
|
<!-- Otherwise there's no search area selected so we'll just leave the search box for them to click. --> |
|
|
|
</span> |
|
|
|
<span v-else-if="isRemoteActive">No projects were found with that search.</span> |
|
|
|
<span v-else-if="isRemoteActive" |
|
|
|
>No projects were found with that search.</span |
|
|
|
> |
|
|
|
</p> |
|
|
|
</div> |
|
|
|
|
|
|
@ -190,14 +193,9 @@ |
|
|
|
|
|
|
|
<script lang="ts"> |
|
|
|
import "leaflet/dist/leaflet.css"; |
|
|
|
import { Map } from "leaflet"; |
|
|
|
import * as L from "leaflet"; |
|
|
|
import { Component, Vue } from "vue-facing-decorator"; |
|
|
|
import { |
|
|
|
LMap, |
|
|
|
LTileLayer, |
|
|
|
LMarker, |
|
|
|
} from "@vue-leaflet/vue-leaflet"; |
|
|
|
import { LMap, LTileLayer } from "@vue-leaflet/vue-leaflet"; |
|
|
|
import { Router } from "vue-router"; |
|
|
|
|
|
|
|
import QuickNav from "@/components/QuickNav.vue"; |
|
|
@ -206,12 +204,20 @@ import ProjectIcon from "@/components/ProjectIcon.vue"; |
|
|
|
import OnboardingDialog from "@/components/OnboardingDialog.vue"; |
|
|
|
import TopMessage from "@/components/TopMessage.vue"; |
|
|
|
import { NotificationIface } from "@/constants/app"; |
|
|
|
import { db, retrieveSettingsForActiveAccount } from "@/db/index"; |
|
|
|
import { |
|
|
|
db, |
|
|
|
logConsoleAndDb, |
|
|
|
retrieveSettingsForActiveAccount, |
|
|
|
} from "@/db/index"; |
|
|
|
import { Contact } from "@/db/tables/contacts"; |
|
|
|
import { BoundingBox } from "@/db/tables/settings"; |
|
|
|
import { didInfo, getHeaders, PlanData } from "@/libs/endorserServer"; |
|
|
|
import { |
|
|
|
didInfo, |
|
|
|
errorStringForLog, |
|
|
|
getHeaders, |
|
|
|
PlanData, |
|
|
|
} from "@/libs/endorserServer"; |
|
|
|
import { OnboardPage, retrieveAccountDids } from "@/libs/util"; |
|
|
|
import { LocationEvent } from "leaflet"; |
|
|
|
|
|
|
|
@Component({ |
|
|
|
components: { |
|
|
@ -484,51 +490,89 @@ export default class DiscoverView extends Vue { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
async onMoveEnd(event: LocationEvent) { |
|
|
|
async onMapReady(map: L.Map) { |
|
|
|
this.requestTiles(map); |
|
|
|
} |
|
|
|
|
|
|
|
// Tried but failed to use other vue-leaflet methods update:zoom and update:bounds |
|
|
|
// To access the from this.$refs, use this.$refs.projectMap.mapObject |
|
|
|
|
|
|
|
async onMoveEnd(event: L.LocationEvent) { |
|
|
|
if (this.zoomedSoDoNotMove) { |
|
|
|
// since a zoom triggers a moveend, too, don't duplicate a tile request |
|
|
|
this.zoomedSoDoNotMove = false; |
|
|
|
} else { |
|
|
|
// not part of a zoom so request tiles |
|
|
|
await this.requestTiles(event); |
|
|
|
await this.requestTiles(event.target); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
async onZoomEnd(event: LocationEvent) { |
|
|
|
await this.requestTiles(event); |
|
|
|
async onZoomEnd(event: L.LocationEvent) { |
|
|
|
await this.requestTiles(event.target); |
|
|
|
} |
|
|
|
|
|
|
|
onZoomStart(event: LocationEvent) { |
|
|
|
onZoomStart(/* event: L.LocationEvent */) { |
|
|
|
this.zoomedSoDoNotMove = true; |
|
|
|
} |
|
|
|
|
|
|
|
async requestTiles(event: LocationEvent) { |
|
|
|
const bounds = event.target.getBounds(); |
|
|
|
const queryParams = [ |
|
|
|
"minLocLat=" + bounds?.getSouthWest().lat, |
|
|
|
"maxLocLat=" + bounds?.getNorthEast().lat, |
|
|
|
"westLocLon=" + bounds?.getSouthWest().lng, |
|
|
|
"eastLocLon=" + bounds?.getNorthEast().lng, |
|
|
|
].join("&"); |
|
|
|
const response = await fetch(this.apiServer + "/api/v2/report/planCountsByBBox?" + queryParams); |
|
|
|
if (response.status === 200) { |
|
|
|
const results = await response.json(); |
|
|
|
if (results.data?.tiles?.length > 0) { |
|
|
|
Object.values(this.markers).forEach(marker => marker.remove()); |
|
|
|
this.markers = {}; |
|
|
|
for (const tile of results.data.tiles) { |
|
|
|
const pinLat = (tile.minFoundLat + tile.maxFoundLat) / 2; |
|
|
|
const pinLon = (tile.minFoundLon + tile.maxFoundLon) / 2; |
|
|
|
const numberIcon = L.divIcon({ |
|
|
|
className: "numbered-marker", |
|
|
|
html: `<strong>${tile.recordCount}</strong>`, |
|
|
|
iconSize: [24, 24], |
|
|
|
iconAnchor: [12, 12], // coordinates of the tip of the icon relative to the top-left corner of the icon |
|
|
|
}); |
|
|
|
const marker = L.marker([pinLat, pinLon], { icon: numberIcon }); |
|
|
|
marker.addTo(event.target); |
|
|
|
this.markers["" + tile.indexLat + "x" + tile.indexLon] = marker; |
|
|
|
async requestTiles(targetMap: L.Map) { |
|
|
|
try { |
|
|
|
const bounds = targetMap.getBounds(); |
|
|
|
const queryParams = [ |
|
|
|
"minLocLat=" + bounds?.getSouthWest().lat, |
|
|
|
"maxLocLat=" + bounds?.getNorthEast().lat, |
|
|
|
"westLocLon=" + bounds?.getSouthWest().lng, |
|
|
|
"eastLocLon=" + bounds?.getNorthEast().lng, |
|
|
|
].join("&"); |
|
|
|
const response = await fetch( |
|
|
|
this.apiServer + "/api/v2/report/planCountsByBBox?" + queryParams, |
|
|
|
); |
|
|
|
if (response.status === 200) { |
|
|
|
const results = await response.json(); |
|
|
|
if (results.data?.tiles?.length > 0) { |
|
|
|
Object.values(this.markers).forEach((marker) => marker.remove()); |
|
|
|
this.markers = {}; |
|
|
|
for (const tile of results.data.tiles) { |
|
|
|
const pinLat = (tile.minFoundLat + tile.maxFoundLat) / 2; |
|
|
|
const pinLon = (tile.minFoundLon + tile.maxFoundLon) / 2; |
|
|
|
const numberIcon = L.divIcon({ |
|
|
|
className: "numbered-marker", |
|
|
|
html: `<strong>${tile.recordCount}</strong>`, |
|
|
|
iconSize: [24, 24], |
|
|
|
// Why isn't this showing? |
|
|
|
iconAnchor: [12, 12], // coordinates of the tip of the icon relative to the top-left corner of the icon |
|
|
|
}); |
|
|
|
const marker = L.marker([pinLat, pinLon], { icon: numberIcon }); |
|
|
|
marker.addTo(targetMap); |
|
|
|
this.markers[ |
|
|
|
"" + tile.indexLat + "X" + tile.indexLon + "*" + tile.recordCount |
|
|
|
] = marker; |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
throw { |
|
|
|
message: "Got an error loading projects on the map.", |
|
|
|
response: { |
|
|
|
status: response.status, |
|
|
|
statusText: response.statusText, |
|
|
|
url: response.url, |
|
|
|
}, |
|
|
|
}; |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
logConsoleAndDb( |
|
|
|
"Error loading projects on the map: " + errorStringForLog(e), |
|
|
|
true, |
|
|
|
); |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: "alert", |
|
|
|
type: "danger", |
|
|
|
title: "Map Error", |
|
|
|
text: "There was a problem loading projects on the map.", |
|
|
|
}, |
|
|
|
3000, |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -612,4 +656,4 @@ export default class DiscoverView extends Vue { |
|
|
|
border-radius: 50%; |
|
|
|
border: 2px solid white; |
|
|
|
} |
|
|
|
</style> |
|
|
|
</style> |
|
|
|