I'm trying to store lat/lng on mouse click to geometry Point.
So basically when user clicks on map i need to get those coordinates(lat/lng) and store them to variable for example point = Point(lat, lng)
so i can calculate nearest geometry and pull data from PostGIS.
I'm aware i need to establish backend and @pyqtSlot(float,float)
, but since im new to this i cant get it to work. I have this code that is generated by QtDesigner and you dont have to bother with all the buttons.
Here is the HTML/JS part:
maphtml = '''
<!DOCTYPE HTML>
<html>
<head>
<meta name="robots" content="index, all" />
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<title>WebGL Earth API - Side-by-side - Basic Leaflet
compatibility</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-
0.7.2/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
<script src="http://www.webglearth.com/v2/api.js"></script>
<script src="scripts/qwebchannel.js"></script>
<script>
var backend;
new QWebChannel(qt.webChannelTransport, function (channel) {
backend = channel.objects.backend;
});
function init() {
var m = {};
start_(L, 'L');
start_(WE, 'WE');
function start_(API, suffix) {
var mapDiv = 'map' + suffix;
var map = API.map(mapDiv, {
center: [51.505, -0.09],
zoom: 4,
dragging: true,
scrollWheelZoom: true,
proxyHost: 'http://srtm.webglearth.com/cgi-bin/corsproxy.fcgi?url='
});
m[suffix] = map;
//Add baselayer
API.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
attribution: '© OpenStreetMap contributors'
}).addTo(map);
//Add TileJSON overlay
var json = {"profile": "mercator", "name": "Grand Canyon USGS", "format":
"png", "bounds": [15.976953506469728, 45.813157465613884], "minzoom": 10,
"version": "1.0.0", "maxzoom": 16, "center": [15.976953506469728,
45.813157465613884, 16], "type": "overlay", "description": "",
"basename":
"grandcanyon", "tilejson": "2.0.0", "sheme": "xyz", "tiles":
["http://tileserver.maptiler.com/grandcanyon/{z}/{x}/{y}.png"]};
if (API.tileLayerJSON) {
var overlay2 = API.tileLayerJSON(json, map);
} else {
//If not able to display the overlay, at least move to the same location
map.setView([json.center[1], json.center[0]], json.center[2]);
}
//Print coordinates of the mouse
map.on('click', function(e) {
document.getElementById('coords').innerHTML = e.latlng.lat + ', ' +
e.latlng.lng;
backend.foo(e.latlng.lat,e.latlng.lng)
});
}
//Synchronize view
m['L'].on('click', function(e) {
var center = m['L'].getCenter();
var zoom = m['L'].getZoom();
m['WE'].setView([center['lat'], center['lng']], zoom);
});
}
</script>
<style>
html, body{padding: 0; margin: 0; overflow: hidden;}
#mapL, #mapWE {position:absolute !important; top: 0; right: 0; bottom: 0;
left: 0;
background-color: #fff; position: absolute !important;}
#mapL {right: 0%;}
#mapWE {left: 100%;}
#coords {position: absolute; bottom: 0;}
</style>
</head>
<body onload="javascript:init()">
<div id="mapL"></div>
<div id="mapWE"></div>
<div id="coords"></div>
</body>
</html>
'''
And here is PyQt code, they are all in same .py btw. At the start i have Backend()
that should be doin its job. You can skip to def setupUI
and to self.mapa = QtWidgets.QWidget(self.centralwidget)
to see where i call Backend()
:
class Backend(QObject):
@pyqtSlot(float,float)
def foo(self, lat,lng):
global x,y
x=lng
y=lat
print(lat, lng)
class Ui_MainWindow(object):
def selected(self, text):
self.selected_text = text
return self.selected_text
def connecti(self):
try:
engine =
create_engine('postgresql://postgres:hrvatina@localhost:5432/Diplomski')
self.connection = engine.connect()
return QMessageBox.information(None, "Uspješna konekcija",
"Spojeni ste na bazu")
except:
return QMessageBox.critical(None, "Pogreška", "Neuspješno
spajanje na bazu")
def odabir(self):
try:
broj = self.link.text()
self.comboBox.activated[str].connect(self.selected)
value = str(self.comboBox.currentText())
if self.pon.isChecked():
radio = str(self.pon.text())
print(radio)
elif self.uto.isChecked():
radio = str(self.uto.text())
print(radio)
elif self.sri.isChecked():
radio = str(self.sri.text())
print(radio)
elif self.cet.isChecked():
radio = str(self.cet.text())
print(radio)
elif self.pet.isChecked():
radio = str(self.pet.text())
print(radio)
elif self.sub.isChecked():
radio = str(self.sub.text())
print(radio)
elif self.ned.isChecked():
radio = str(self.ned.text())
print(radio)
query ='SELECT * FROM ' + radio + '_' + value + ' where "IdLink"
= %s' % (broj)
df = pd.read_sql_query(query, con=self.connection, params=
{'link': '%' + broj + '%'})
df = df.transpose()
hf = df.join(df.iloc[:, 0].str.split(';', 3,
expand=True).rename(columns={0: 'mean', 1: 'median', 2: 'std'}))
g = hf.drop(hf.columns[[0]], axis=1)
hm = g.drop(g.index[[0]])
prva1 = hm.apply(pd.to_numeric, errors='coerce')
#Izračun srednjih vrijednosti
mean=prva1['mean'].mean()
self.mean.setText(str(mean))
median=prva1['median'].mean()
self.median.setText(str(median))
std=prva1['std'].mean()
self.std.setText(str(std))
upper_bound = go.Scatter(
name='Gornja granica',
x=prva1.index.to_native_types(),
y=prva1['mean'] + prva1['std'],
mode='lines',
marker=dict(color="028F1E"),
line=dict(width=1, color="028F1E"),
fillcolor='rgba(68, 68, 68, 0.3)',
fill='tonexty')
trace = go.Scatter(
name='Srednja vrijednost',
x=prva1.index.to_native_types(),
y=prva1['mean'],
mode='lines',
line=dict(color='rgb(31, 119, 180)'),
fillcolor='rgba(68, 68, 68, 0.3)',
fill='tonexty')
lower_bound = go.Scatter(
name='Donja granica',
x=prva1.index.to_native_types(),
y=prva1['mean'] - prva1['std'],
marker=dict(color="9B111E"),
line=dict(width=1, color="9B111E"),
mode='lines')
data = [lower_bound, trace, upper_bound]
layout = go.Layout(
xaxis=dict(
title='AXIS TITLE',
titlefont=dict(
family='Arial, sans-serif',
size=18,
color='lightgrey'
),
showticklabels=True,
tickangle=45,
tickfont=dict(
family='Old Standard TT, serif',
size=14,
color='black'
),
exponentformat='e',
showexponent='All'
),
yaxis=dict(title='Brzina (km/h)'),
title='Minutni interval za ' + radio + ' sa standardnom
devijacijom za ' + broj,
)
fig = go.Figure(data=data, layout=layout)
raw_html = '<html><head><meta charset="utf-8" />'
raw_html += '<script src="https://cdn.plot.ly/plotly-
latest.min.js"></script></head>'
raw_html += '<body>'
raw_html += plotly.offline.plot(fig, include_plotlyjs=False,
output_type='div')
raw_html += '</body></html>'
#PLOTLY
self.graf.setHtml(raw_html)
except:
return QMessageBox.critical(None, "Pogreška", "Podaci za odabrane
opcije ne postoje!")
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(978, 704)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.formLayout = QtWidgets.QFormLayout(self.centralwidget)
self.formLayout.setObjectName("formLayout")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.gumb_spoji = QtWidgets.QPushButton(self.centralwidget)
self.gumb_spoji.setObjectName("gumb_spoji")
# Spoji se na bazu
self.gumb_spoji.clicked.connect(self.connecti)
engine =
self.verticalLayout.addWidget(self.gumb_spoji)
self.link = QtWidgets.QLineEdit(self.centralwidget)
self.link.setObjectName("link")
self.verticalLayout.addWidget(self.link)
self.pon = QtWidgets.QRadioButton(self.centralwidget)
self.pon.setObjectName("pon")
self.verticalLayout.addWidget(self.pon)
self.uto = QtWidgets.QRadioButton(self.centralwidget)
self.uto.setObjectName("uto")
self.verticalLayout.addWidget(self.uto)
self.sri = QtWidgets.QRadioButton(self.centralwidget)
self.sri.setObjectName("sri")
self.verticalLayout.addWidget(self.sri)
self.cet = QtWidgets.QRadioButton(self.centralwidget)
self.cet.setObjectName("cet")
self.verticalLayout.addWidget(self.cet)
self.pet = QtWidgets.QRadioButton(self.centralwidget)
self.pet.setObjectName("pet")
self.verticalLayout.addWidget(self.pet)
self.sub = QtWidgets.QRadioButton(self.centralwidget)
self.sub.setObjectName("sub")
self.verticalLayout.addWidget(self.sub)
self.ned = QtWidgets.QRadioButton(self.centralwidget)
self.ned.setObjectName("ned")
self.verticalLayout.addWidget(self.ned)
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.verticalLayout.addWidget(self.comboBox)
self.gumb_dohvati = QtWidgets.QPushButton(self.centralwidget)
self.gumb_dohvati.setObjectName("gumb_dohvati")
# Dohvati podatke
self.gumb_dohvati.clicked.connect(self.odabir)
self.verticalLayout.addWidget(self.gumb_dohvati)
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setObjectName("label")
self.verticalLayout.addWidget(self.label)
self.mean = QtWidgets.QLineEdit(self.centralwidget)
self.mean.setObjectName("mean")
self.verticalLayout.addWidget(self.mean)
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setObjectName("label_2")
self.verticalLayout.addWidget(self.label_2)
self.median = QtWidgets.QLineEdit(self.centralwidget)
self.median.setObjectName("median")
self.verticalLayout.addWidget(self.median)
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setObjectName("label_3")
self.verticalLayout.addWidget(self.label_3)
self.std = QtWidgets.QLineEdit(self.centralwidget)
self.std.setObjectName("std")
self.verticalLayout.addWidget(self.std)
self.formLayout.setLayout(0, QtWidgets.QFormLayout.LabelRole,
self.verticalLayout)
# I want to pass lat/lng here and after user click show data in
Qwidget below, but i will do that on my own i just need those lat/lng
after every click
self.mapa = QtWidgets.QWidget(self.centralwidget)
self.mapa.setObjectName("mapa")
backend = Backend()
channel = QWebChannel()
channel.registerObject('backend', backend)
self.mapa = QWebEngineView()
self.mapa.page().setWebChannel(channel)
self.mapa.setHtml(maphtml)
point=Point(y,x)
#sql = 'select * from geo'
#df = gpd.GeoDataFrame.from_postgis(sql, self.connection,
geom_col='geometry' )
#def min_dist(point, gpd2):
#df['Dist'] = df.apply(lambda row:
point.distance(row.geometry),axis=1)
#geoseries = df.iloc[gpd2['Dist'].idxmin()]
#return geoseries['IdLink']
#point=Point(self.Backend.lat(),self.Backend.lng())
#self.brojka=min_dist(point,df)
self.formLayout_3 = QtWidgets.QFormLayout(self.mapa)
self.formLayout_3.setObjectName("formLayout_3")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole,
self.mapa)
self.graf = QtWidgets.QWidget(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred,
QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(255)
sizePolicy.setHeightForWidth(self.graf.sizePolicy().hasHeightForWidth())
self.graf.setSizePolicy(sizePolicy)
self.graf.setMouseTracking(True)
self.graf.setObjectName("graf")
self.graf = QWebEngineView(self.centralwidget)
self.formLayout_2 = QtWidgets.QFormLayout(self.graf)
self.formLayout_2.setObjectName("formLayout_2")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.SpanningRole,
self.graf)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 978, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.gumb_spoji.setText(_translate("MainWindow", "Spoji se na bazu"))
self.pon.setText(_translate("MainWindow", "Ponedjeljak"))
self.uto.setText(_translate("MainWindow", "Utorak"))
self.sri.setText(_translate("MainWindow", "Srijeda"))
self.cet.setText(_translate("MainWindow", "Cetvrtak"))
self.pet.setText(_translate("MainWindow", "Petak"))
self.sub.setText(_translate("MainWindow", "Subota"))
self.ned.setText(_translate("MainWindow", "Nedjelja"))
self.comboBox.setItemText(0, _translate("MainWindow", "5min"))
self.comboBox.setItemText(1, _translate("MainWindow", "15min"))
self.gumb_dohvati.setText(_translate("MainWindow", "Dohvati
podatke"))
self.label.setText(_translate("MainWindow", "Srednja vrijednost"))
self.label_2.setText(_translate("MainWindow", "Median"))
self.label_3.setText(_translate("MainWindow", "Standardna
devijacija"))
if __name__ == "__main__":
import sys
app = QtCore.QCoreApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
I saw similar questions but couldn't make it work so far... In PyQt4 i have it almost working, but since i am bad at JS, i dont know how to to return lat/lng intead of getCenter().lat from this code:
if(typeof MainWindow != 'undefined') {
var onMapMove = function(e) { MainWindow.onMapMove(map.getCenter().lat,
map.getCenter().lng) };
map.on('click', onMapMove);
onMapMove();
I can not work much with your code because it is badly formatted and there are missing elements to define so my answer will be based on showing you an example.
First of all it does not put all the code in a file, that makes it difficult to maintain the code, in this case I have divided into the following files:
.
├── index.html
├── main.py
├── utils.css
└── utils.js
window.onload = function() {
new QWebChannel(qt.webChannelTransport, function (channel) {
window.backend = channel.objects.backend;
});
var m = {};
start_(L, 'L');
start_(WE, 'WE');
function start_(API, suffix) {
var mapDiv = 'map' + suffix;
var map = API.map(mapDiv, {
center: [51.505, -0.09],
zoom: 4,
dragging: true,
scrollWheelZoom: true,
proxyHost: 'http://srtm.webglearth.com/cgi-bin/corsproxy.fcgi?url='
});
m[suffix] = map;
//Add baselayer
API.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
//Add TileJSON overlay
var json = {
"profile": "mercator",
"name": "Grand Canyon USGS",
"format": "png",
"bounds": [15.976953506469728, 45.813157465613884],
"minzoom": 10,
"version": "1.0.0",
"maxzoom": 16,
"center": [15.976953506469728,
45.813157465613884, 16
],
"type": "overlay",
"description": "",
"basename": "grandcanyon",
"tilejson": "2.0.0",
"sheme": "xyz",
"tiles": ["http://tileserver.maptiler.com/grandcanyon/{z}/{x}/{y}.png"]
};
if (API.tileLayerJSON) {
var overlay2 = API.tileLayerJSON(json, map);
} else {
//If not able to display the overlay, at least move to the same location
map.setView([json.center[1], json.center[0]], json.center[2]);
}
//Print coordinates of the mouse
map.on('click', function(e) {
document.getElementById('coords').innerHTML = e.latlng.lat + ', ' + e.latlng.lng;
backend.pointClicked(e.latlng.lat, e.latlng.lng);
});
}
//Synchronize view
m['L'].on('click', function(e) {
var center = m['L'].getCenter();
var zoom = m['L'].getZoom();
m['WE'].setView([center['lat'], center['lng']], zoom);
});
}
html, body {
padding: 0;
margin: 0;
overflow: hidden;
}
#mapL, #mapWE {
position: absolute !important;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: #fff;
position: absolute !important;
}
#mapL {
right: 0%;
}
#mapWE {
left: 100%;
}
#coords {
position: absolute;
bottom: 0;
}
<!DOCTYPE HTML>
<html>
<head>
<meta name="robots" content="index, all"/>
<title>WebGL Earth API - Side-by-side - Basic Leaflet compatibility
</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css"/>
<link rel="stylesheet" href="utils.css"/>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script type="text/javascript" src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
<script type="text/javascript" src="http://www.webglearth.com/v2/api.js"></script>
<script type="text/javascript" src="utils.js"> </script>
</head>
<body>
<div id="mapL"></div>
<div id="mapWE"></div>
<div id="coords"></div>
</body>
</html>
main.py
from PyQt5 import QtWebEngineWidgets, QtWidgets, QtCore, QtWebChannel
class Backend(QtCore.QObject):
pointChanged = QtCore.pyqtSignal(float, float)
@QtCore.pyqtSlot(float,float)
def pointClicked(self, x, y):
self.pointChanged.emit(x, y)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
map_view = QtWebEngineWidgets.QWebEngineView()
backend = Backend(self)
backend.pointChanged.connect(self.onPointChanged)
channel = QtWebChannel.QWebChannel(self)
channel.registerObject('backend', backend)
map_view.page().setWebChannel(channel)
file = QtCore.QDir.current().absoluteFilePath("index.html")
map_view.load(QtCore.QUrl.fromLocalFile(file))
self.setCentralWidget(map_view)
@QtCore.pyqtSlot(float,float)
def onPointChanged(self, x, y):
print("new points")
print(x, y)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())