Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(MySQL): spatial fields support #165

Merged
merged 8 commits into from
Jan 30, 2022
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,16 @@
"dependencies": {
"@electron/remote": "^2.0.1",
"@mdi/font": "^6.1.95",
"@turf/helpers": "^6.5.0",
"@vscode/vscode-languagedetection": "^1.0.21",
"ace-builds": "^1.4.13",
"better-sqlite3": "^7.4.4",
"electron-log": "^4.4.1",
"electron-store": "^8.0.1",
"electron-updater": "^4.6.1",
"electron-window-state": "^5.0.3",
"faker": "~5.5.3",
"faker": "^5.5.3",
"leaflet": "^1.7.1",
"marked": "^4.0.0",
"moment": "^2.29.1",
"mysql2": "^2.3.2",
Expand Down Expand Up @@ -150,7 +152,7 @@
"html-webpack-plugin": "^5.5.0",
"mini-css-extract-plugin": "~2.4.5",
"node-loader": "^2.0.0",
"playwright": "^1.16.3",
"playwright": "^1.18.1",
"progress-webpack-plugin": "^1.0.12",
"sass": "^1.42.1",
"sass-loader": "^12.3.0",
Expand Down
18 changes: 9 additions & 9 deletions src/common/data-types/mysql.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,56 +219,56 @@ module.exports = [
types: [
{
name: 'POINT',
length: true,
length: false,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'LINESTRING',
length: true,
length: false,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'POLYGON',
length: true,
length: false,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'GEOMETRY',
length: true,
length: false,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'MULTIPOINT',
length: true,
length: false,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'MULTILINESTRING',
length: true,
length: false,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'MULTIPOLYGON',
length: true,
length: false,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'GEOMETRYCOLLECTION',
length: true,
name: 'GEOMCOLLECTION',
length: false,
collation: false,
unsigned: false,
zerofill: false
Expand Down
25 changes: 24 additions & 1 deletion src/common/fieldTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ export const TEXT = [
export const LONG_TEXT = [
'TEXT',
'MEDIUMTEXT',
'LONGTEXT'
'LONGTEXT',
'JSON',
'VARBINARY'
];

export const ARRAY = [
Expand Down Expand Up @@ -82,3 +84,24 @@ export const BIT = [
'BIT',
'BIT VARYING'
];

export const SPATIAL = [
'POINT',
'LINESTRING',
'POLYGON',
'GEOMETRY',
'MULTIPOINT',
'MULTILINESTRING',
'MULTIPOLYGON',
'GEOMCOLLECTION',
'GEOMETRYCOLLECTION'
];

// Used to check multi spatial fields only
export const IS_MULTI_SPATIAL = [
'MULTIPOINT',
'MULTILINESTRING',
'MULTIPOLYGON',
'GEOMCOLLECTION',
'GEOMETRYCOLLECTION'
];
10 changes: 10 additions & 0 deletions src/common/libs/getArrayDepth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
*
* @param {any[]} array
* @returns {number}
*/
export function getArrayDepth (array) {
return Array.isArray(array)
? 1 + Math.max(0, ...array.map(getArrayDepth))
: 0;
}
108 changes: 108 additions & 0 deletions src/renderer/components/BaseMap.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<template>
<div id="map" class="map" />
</template>
<script>
import L from 'leaflet';
import {
point,
lineString,
polygon
} from '@turf/helpers';
import { getArrayDepth } from 'common/libs/getArrayDepth';

export default {
name: 'BaseMap',
props: {
points: [Object, Array],
isMultiSpatial: Boolean
},
data () {
return {
map: null,
markers: [],
center: null
};
},
mounted () {
if (this.isMultiSpatial) {
for (const element of this.points)
this.markers.push(this.getMarkers(element));
}
else {
this.markers = this.getMarkers(this.points);

if (!Array.isArray(this.points))
this.center = [this.points.y, this.points.x];
}

this.map = L.map('map', {
center: this.center || [0, 0],
zoom: 15,
minZoom: 1,
attributionControl: false
});

L.control.attribution({ prefix: '<b>Leaflet</b>' }).addTo(this.map);

const geoJsonObj = L.geoJSON(this.markers, {
style: function () {
return {
weight: 2,
fillColor: '#ff7800',
color: '#ff7800',
opacity: 0.8,
fillOpacity: 0.4
};
},
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, {
radius: 7,
weight: 2,
fillColor: '#ff7800',
color: '#ff7800',
opacity: 0.8,
fillOpacity: 0.4
});
}
}).addTo(this.map);

const southWest = L.latLng(-90, -180);
const northEast = L.latLng(90, 180);
const bounds = L.latLngBounds(southWest, northEast);
this.map.setMaxBounds(bounds);

if (!this.center) this.map.fitBounds(geoJsonObj.getBounds());

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <b>OpenStreetMap</b>'
}).addTo(this.map);
},
methods: {
getMarkers (points) {
if (Array.isArray(points)) {
if (getArrayDepth(points) === 1)
return lineString(points.reduce((acc, curr) => [...acc, [curr.x, curr.y]], []));
else
return polygon(points.map(arr => arr.reduce((acc, curr) => [...acc, [curr.x, curr.y]], [])));
}
else
return point([points.x, points.y]);
}
}
};
</script>

<style lang="scss">
.map{
height: 400px;
}

.marker-icon{
display: flex;
justify-content: center;
align-items: center;
background: $primary-color;
border-radius: 50%;
box-shadow: 0 0 5px 1px darken($body-font-color-dark, 40%);
}
</style>
4 changes: 3 additions & 1 deletion src/renderer/components/WorkspaceTabQueryTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,9 @@ export default {
`${this.fields[0].table}.${this.selectedCell.field}`,
`${this.fields[0].tableAlias}.${this.selectedCell.field}`
].includes(prop));
const valueToCopy = row[cellName];
let valueToCopy = row[cellName];
if (typeof valueToCopy === 'object')
valueToCopy = JSON.stringify(valueToCopy);
navigator.clipboard.writeText(valueToCopy);
},
copyRow () {
Expand Down
60 changes: 56 additions & 4 deletions src/renderer/components/WorkspaceTabQueryTableRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,21 @@
</div>
</template>
</ConfirmModal>
<ConfirmModal
v-if="isMapModal"
:hide-footer="true"
size="medium"
@hide="hideEditorModal"
>
<template #header>
<div class="d-flex">
<i class="mdi mdi-24px mdi-map mr-1" /> <span class="cut-text">"{{ editingField }}"</span>
</div>
</template>
<template #body>
<BaseMap :points="editingContent" :is-multi-spatial="isMultiSpatial" />
</template>
</ConfirmModal>
<ConfirmModal
v-if="isBlobEditor"
:confirm-text="$t('word.update')"
Expand Down Expand Up @@ -187,18 +202,36 @@ import { mimeFromHex } from 'common/libs/mimeFromHex';
import { formatBytes } from 'common/libs/formatBytes';
import { bufferToBase64 } from 'common/libs/bufferToBase64';
import hexToBinary from 'common/libs/hexToBinary';
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BOOLEAN, DATE, TIME, DATETIME, BLOB, BIT, HAS_TIMEZONE } from 'common/fieldTypes';
import {
TEXT,
LONG_TEXT,
ARRAY,
TEXT_SEARCH,
NUMBER,
FLOAT,
BOOLEAN,
DATE,
TIME,
DATETIME,
BLOB,
BIT,
HAS_TIMEZONE,
SPATIAL,
IS_MULTI_SPATIAL
} from 'common/fieldTypes';
import { VueMaskDirective } from 'v-mask';
import ConfirmModal from '@/components/BaseConfirmModal';
import TextEditor from '@/components/BaseTextEditor';
import BaseMap from '@/components/BaseMap';
import ForeignKeySelect from '@/components/ForeignKeySelect';

export default {
name: 'WorkspaceTabQueryTableRow',
components: {
ConfirmModal,
TextEditor,
ForeignKeySelect
ForeignKeySelect,
BaseMap
},
directives: {
mask: VueMaskDirective
Expand Down Expand Up @@ -248,7 +281,10 @@ export default {
return val;
}

return val;
if (SPATIAL.includes(type))
return val;

return typeof val === 'object' ? JSON.stringify(val) : val;
}
},
props: {
Expand All @@ -263,6 +299,8 @@ export default {
isInlineEditor: {},
isTextareaEditor: false,
isBlobEditor: false,
isMapModal: false,
isMultiSpatial: false,
willBeDeleted: false,
originalContent: null,
editingContent: null,
Expand Down Expand Up @@ -331,6 +369,9 @@ export default {
if (BOOLEAN.includes(this.editingType))
return { type: 'boolean', mask: false };

if (SPATIAL.includes(this.editingType))
return { type: 'map', mask: false };

return { type: 'text', mask: false };
},
isImage () {
Expand Down Expand Up @@ -403,7 +444,7 @@ export default {
return bufferToBase64(val);
},
editON (event, content, field) {
if (!this.isEditable) return;
if (!this.isEditable || this.editingType === 'none') return;

window.addEventListener('keydown', this.onKey);

Expand All @@ -419,6 +460,15 @@ export default {
return;
}

if (SPATIAL.includes(type)) {
if (content) {
this.isMultiSpatial = IS_MULTI_SPATIAL.includes(type);
this.isMapModal = true;
this.editingContent = this.$options.filters.typeFormat(content, type);
}
return;
}

if (BLOB.includes(type)) {
this.isBlobEditor = true;
this.editingContent = content || '';
Expand Down Expand Up @@ -490,6 +540,8 @@ export default {
hideEditorModal () {
this.isTextareaEditor = false;
this.isBlobEditor = false;
this.isMapModal = false;
this.isMultiSpatial = false;
},
downloadFile () {
const downloadLink = document.createElement('a');
Expand Down
1 change: 1 addition & 0 deletions src/renderer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import Vue from 'vue';

import '@mdi/font/css/materialdesignicons.css';
import 'leaflet/dist/leaflet.css';
import '@/scss/main.scss';

import App from '@/App.vue';
Expand Down
Loading