map on standalone tab

This commit is contained in:
Jan Prochazka
2022-06-11 17:57:18 +02:00
parent 427e25b3c0
commit 6e1a1edac0
7 changed files with 208 additions and 157 deletions

View File

@@ -1,152 +1,7 @@
<script lang="ts">
import _ from 'lodash';
import { onMount, tick } from 'svelte';
import 'leaflet/dist/leaflet.css';
import leaflet from 'leaflet';
import wellknown from 'wellknown';
import { isWktGeometry } from 'dbgate-tools';
import resizeObserver from '../utility/resizeObserver';
// import Map from 'ol/Map';
// import View from 'ol/View';
// import TileLayer from 'ol/layer/Tile';
// import XYZ from 'ol/source/XYZ';
import MapView from '../elements/MapView.svelte';
export let selection;
export let wrap;
let refContainer;
let map;
let selectionLayers = [];
function createColumnsTable(cells) {
return `<table>${cells.map(cell => `<tr><td>${cell.column}</td><td>${cell.value}</td></tr>`).join('\n')}</table>`;
}
function addSelectionToMap() {
if (!map) return;
if (!selection) return;
for (const selectionLayer of selectionLayers) {
selectionLayer.remove();
}
selectionLayers = [];
const selectedRows = _.groupBy(selection || [], 'row');
const features = [];
for (const rowKey of _.keys(selectedRows)) {
const cells = selectedRows[rowKey];
const lat = cells.find(x => x.column.toLowerCase().includes('lat'));
const lon = cells.find(x => x.column.toLowerCase().includes('lon') || x.column.toLowerCase().includes('lng'));
const geoValues = cells.map(x => x.value).filter(isWktGeometry);
if (lat && lon) {
features.push({
type: 'Feature',
properties: {
popupContent: createColumnsTable(cells),
},
geometry: {
type: 'Point',
coordinates: [lon.value, lat.value],
},
});
}
if (geoValues.length > 0) {
// parse WKT to geoJSON array
features.push(
...geoValues.map(wellknown).map(geometry => ({
type: 'Feature',
properties: {
popupContent: createColumnsTable(cells.filter(x => !isWktGeometry(x.value))),
},
geometry,
}))
);
}
}
if (features.length == 0) {
return;
}
const geoJson = {
type: 'FeatureCollection',
features,
};
const geoJsonObj = leaflet
.geoJSON(geoJson, {
style: function () {
return {
weight: 2,
fillColor: '#ff7800',
color: '#ff7800',
opacity: 0.8,
fillOpacity: 0.4,
};
},
pointToLayer: (feature, latlng) => {
return leaflet.circleMarker(latlng, {
radius: 7,
weight: 2,
fillColor: '#ff7800',
color: '#ff7800',
opacity: 0.8,
fillOpacity: 0.4,
});
},
onEachFeature: (feature, layer) => {
// does this feature have a property named popupContent?
if (feature.properties && feature.properties.popupContent) {
layer.bindPopup(feature.properties.popupContent);
}
},
})
.addTo(map);
// geoJsonObj.bindPopup('This is the Transamerica Pyramid'); //.openPopup();
map.fitBounds(geoJsonObj.getBounds());
selectionLayers.push(geoJsonObj);
}
onMount(() => {
map = leaflet.map(refContainer).setView([50, 15], 13);
leaflet
.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap',
})
.addTo(map);
addSelectionToMap();
// map.fitBounds([
// [50, 15],
// [50.1, 15],
// [50, 15.1],
// ]);
// const marker = leaflet.marker([50, 15]).addTo(map);
// <div bind:this={refContainer} class="flex1 map-container" />
});
$: {
selection;
addSelectionToMap();
}
</script>
<div
bind:this={refContainer}
class="flex1"
use:resizeObserver={true}
on:resize={async e => {
await tick();
map.invalidateSize();
}}
/>
<MapView {selection} />

View File

@@ -112,6 +112,14 @@
onClick: () => getCurrentDataGrid().editJsonDocument(),
});
registerCommand({
id: 'dataGrid.openSelectionInMap',
category: 'Data grid',
name: 'Open selection in map',
testEnabled: () => getCurrentDataGrid() != null, // ?.openSelectionInMapEnabled(),
onClick: () => getCurrentDataGrid().openSelectionInMap(),
});
registerCommand({
id: 'dataGrid.viewJsonDocument',
category: 'Data grid',
@@ -306,6 +314,8 @@
import { apiCall } from '../utility/api';
import getElectron from '../utility/getElectron';
import { isCtrlOrCommandKey, isMac } from '../utility/common';
import { selectionCouldBeShownOnMap } from '../elements/MapView.svelte';
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
export let onLoadNextData = undefined;
export let grider = undefined;
@@ -530,6 +540,23 @@
openJsonDocument(json);
}
export function openSelectionInMap() {
const selection = getCellsPublished(selectedCells);
if (!selectionCouldBeShownOnMap(selection)) {
showModal(ErrorMessageModal, { message: 'There is nothing to be shown on map' });
return;
}
openNewTab({
title: 'Map',
icon: 'img map',
tabComponent: 'MapTab',
props: {
selection,
},
});
return;
}
function getSelectedExportableCell() {
const electron = getElectron();
if (electron && selectedCells.length == 1) {
@@ -1426,6 +1453,7 @@
{ command: 'dataGrid.generateSqlFromData' },
{ command: 'dataGrid.openFreeTable' },
{ command: 'dataGrid.openChartFromSelection' },
{ command: 'dataGrid.openSelectionInMap', hideDisabled: true },
{ placeTag: 'chart' }
);

View File

@@ -0,0 +1,164 @@
<script lang="ts" context="module">
export function selectionCouldBeShownOnMap(selection) {
console.log('selection', selection);
if (selection.length > 0 && _.find(selection, x => isWktGeometry(x.value))) {
return true;
}
if (
selection.find(x => x.column.toLowerCase().includes('lat')) &&
(selection.find(x => x.column.toLowerCase().includes('lon')) ||
selection.find(x => x.column.toLowerCase().includes('lng')))
) {
return true;
}
return false;
}
</script>
<script lang="ts">
import _ from 'lodash';
import { onMount, tick } from 'svelte';
import 'leaflet/dist/leaflet.css';
import leaflet from 'leaflet';
import wellknown from 'wellknown';
import { isWktGeometry } from 'dbgate-tools';
import resizeObserver from '../utility/resizeObserver';
export let selection;
let refContainer;
let map;
let selectionLayers = [];
function createColumnsTable(cells) {
return `<table>${cells.map(cell => `<tr><td>${cell.column}</td><td>${cell.value}</td></tr>`).join('\n')}</table>`;
}
function addSelectionToMap() {
if (!map) return;
if (!selection) return;
for (const selectionLayer of selectionLayers) {
selectionLayer.remove();
}
selectionLayers = [];
const selectedRows = _.groupBy(selection || [], 'row');
const features = [];
for (const rowKey of _.keys(selectedRows)) {
const cells = selectedRows[rowKey];
const lat = cells.find(x => x.column.toLowerCase().includes('lat'));
const lon = cells.find(x => x.column.toLowerCase().includes('lon') || x.column.toLowerCase().includes('lng'));
const geoValues = cells.map(x => x.value).filter(isWktGeometry);
if (lat && lon) {
features.push({
type: 'Feature',
properties: {
popupContent: createColumnsTable(cells),
},
geometry: {
type: 'Point',
coordinates: [lon.value, lat.value],
},
});
}
if (geoValues.length > 0) {
// parse WKT to geoJSON array
features.push(
...geoValues.map(wellknown).map(geometry => ({
type: 'Feature',
properties: {
popupContent: createColumnsTable(cells.filter(x => !isWktGeometry(x.value))),
},
geometry,
}))
);
}
}
if (features.length == 0) {
return;
}
const geoJson = {
type: 'FeatureCollection',
features,
};
const geoJsonObj = leaflet
.geoJSON(geoJson, {
style: function () {
return {
weight: 2,
fillColor: '#ff7800',
color: '#ff7800',
opacity: 0.8,
fillOpacity: 0.4,
};
},
pointToLayer: (feature, latlng) => {
return leaflet.circleMarker(latlng, {
radius: 7,
weight: 2,
fillColor: '#ff7800',
color: '#ff7800',
opacity: 0.8,
fillOpacity: 0.4,
});
},
onEachFeature: (feature, layer) => {
// does this feature have a property named popupContent?
if (feature.properties && feature.properties.popupContent) {
layer.bindPopup(feature.properties.popupContent);
}
},
})
.addTo(map);
// geoJsonObj.bindPopup('This is the Transamerica Pyramid'); //.openPopup();
map.fitBounds(geoJsonObj.getBounds());
selectionLayers.push(geoJsonObj);
}
onMount(() => {
map = leaflet.map(refContainer).setView([50, 15], 13);
leaflet
.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap',
})
.addTo(map);
addSelectionToMap();
// map.fitBounds([
// [50, 15],
// [50.1, 15],
// [50, 15.1],
// ]);
// const marker = leaflet.marker([50, 15]).addTo(map);
// <div bind:this={refContainer} class="flex1 map-container" />
});
$: {
selection;
addSelectionToMap();
}
</script>
<div
bind:this={refContainer}
class="flex1"
use:resizeObserver={true}
on:resize={async e => {
await tick();
map.invalidateSize();
}}
/>

View File

@@ -165,6 +165,7 @@
'img sort-asc': 'mdi mdi-sort-alphabetical-ascending color-icon-green',
'img sort-desc': 'mdi mdi-sort-alphabetical-descending color-icon-green',
'img map': 'mdi mdi-map color-icon-blue',
'img reference': 'mdi mdi-link-box',
'img link': 'mdi mdi-link',

View File

@@ -0,0 +1,8 @@
<script lang="ts">
import MapView from '../elements/MapView.svelte';
export let selection;
</script>
<MapView {selection} />

View File

@@ -24,6 +24,7 @@ import * as DiagramTab from './DiagramTab.svelte';
import * as DbKeyDetailTab from './DbKeyDetailTab.svelte';
import * as QueryDataTab from './QueryDataTab.svelte';
import * as ConnectionTab from './ConnectionTab.svelte';
import * as MapTab from './MapTab.svelte';
export default {
TableDataTab,
@@ -52,4 +53,5 @@ export default {
DbKeyDetailTab,
QueryDataTab,
ConnectionTab,
MapTab,
};

View File

@@ -1,5 +1,5 @@
<script lang="ts" context="module">
import { isWktGeometry } from 'dbgate-tools';
import { isWktGeometry } from 'dbgate-tools';
const formats = [
{
@@ -51,15 +51,7 @@ import { isWktGeometry } from 'dbgate-tools';
return 'jsonRow';
}
if (selection.length > 0 && _.find(selection, x => isWktGeometry(x.value))) {
return 'map';
}
if (
selection.find(x => x.column.toLowerCase().includes('lat')) &&
(selection.find(x => x.column.toLowerCase().includes('lon')) ||
selection.find(x => x.column.toLowerCase().includes('lng')))
) {
if (selectionCouldBeShownOnMap(selection)) {
return 'map';
}
@@ -88,6 +80,7 @@ import { isWktGeometry } from 'dbgate-tools';
import TextCellViewNoWrap from '../celldata/TextCellViewNoWrap.svelte';
import TextCellViewWrap from '../celldata/TextCellViewWrap.svelte';
import ErrorInfo from '../elements/ErrorInfo.svelte';
import { selectionCouldBeShownOnMap } from '../elements/MapView.svelte';
import SelectField from '../forms/SelectField.svelte';
import { selectedCellsCallback } from '../stores';
import WidgetTitle from './WidgetTitle.svelte';