Customizing Qt QML Map using Google maps plugins - qt

I am trying to customize QML Map using Google Maps. I am using this plugin by vladest, which adds Google Map support for QtPositioning Map and works as expected, but I didn't find out how to customize it.
I have tried to parametrize Plugin in QML using PluginParameter:
Plugin {
id: mapPlugin
name: "googlemaps"
// specify plugin parameters if necessary
//PluginParameter {
// name:"googlemaps.maps.apikey"
// value:"<--------->"
//}
//PluginParameter {
// name:"googlemaps.maps.identifier" // I have also tried mapId & map_id instead of identifier
// value: "8e0a97af9386fef"
//}
//PluginParameter {
// name:"googlemaps.maps.identifier"
// value: "8e0a97af9386fef"
//}
//PluginParameter {
// name:"googlemaps.identifier"
// value: "8e0a97af9386fef"
//}
//PluginParameter {
// name:"googlemaps.identifier"
// value: "8e0a97af9386fef"
//}
//PluginParameter {
// name:"identifier"
// value: "8e0a97af9386fef"
//}
//PluginParameter {
// name:"identifier"
// value: "8e0a97af9386fef"
//}
//PluginParameter {
// name:"googlemaps.maps.Map.identifier"
// value: "8e0a97af9386fef"
//}
//PluginParameter {
// name:"googlemaps.maps.map.identifier"
// value: "8e0a97af9386fef"
//}
//PluginParameter {
// name:"googlemaps.maps.map.identifier"
// value: "8e0a97af9386fef"
}
Map {
anchors.fill: parent
plugin: mapPlugin
}
I also studied Google API via styling and I have also tried example of styling via mapId, but didn't manage to make it work.
I have also tried to edit plugin code and add map ID into tile request query, but without success.
Is there any way to customize map?

Related

How to show 3D real buildings in QML maps

I am using Mapbox to work with maps in *.qml. I wanna know if there is any 3D terrain real option like the one that works in iOS maps. The code part I have is,
id: map
plugin: Plugin {
name: "mapboxgl"
PluginParameter {
name: "mapboxgl.access_token";
value: "pk.eyJ1IjoidGFudHJpZG8iLCJhIjoiY2tlYnB0YWo0MGFpczJzcnZubHRlNTAwbiJ9.6QG-4BeuCpUjaawDiyyfVg"
}
PluginParameter {
name: "mapboxgl.mapping.additional_style_urls";
value: "mapbox://styles/tantrido/ckyuch3ub001q16ofjwsxnlz6"
}
}
MapParameter {
type: "layer"
property var name: "3d-buildings"
property var source: "composite"
property var sourceLayer: "building"
property var layerType: "fill-extrusion"
property var minzoom: 15.0
}
MapParameter {
type: "filter"
property var layer: "3d-buildings"
property var filter: [ "==", "extrude", "true" ]
}
MapParameter {
type: "paint"
property var layer: "3d-buildings"
property var fillExtrusionColor: "#f2bc68"
property var fillExtrusionOpacity: 0.6
property var fillExtrusionHeight: { return { type: "identity", property: "height" } }
property var fillExtrusionBase: { return { type: "identity", property: "min_height" } }
}
anchors.fill: parent
center: QtPositioning.coordinate(homeLat.text, homeLng.text)
tilt: 80
zoomLevel: 18
}
This, however, is only a 3D object on any building, not the real one, and the result is like this,
the current QML result
And the one, I am looking for is like this,
Apple 3D map that I am trying to implement in QML

How do you get the currently active notebook name in JupyterLab?

I'm working on creating a server-side extension in JupyterLab and have been searching for quite a while for a way to get the currently active notebook name inside my index.ts file. I found this existing extension that gets the currently active tab name with ILabShell and sets the browser tab to have the same name. In my case I'll be triggering the process with a button on the notebook toolbar so the active tab will always be a notebook. However, when I try to use the code in the activate section of my extension, I get TypeError: labShell.currentChanged is undefined where labShell is an instance of ILabShell. That extension doesn't support JupyterLab 3.0+ so I believe that's part of the problem. However, currentChanged for ILabShell is clearly defined here. What if anything can I change to make it work? Is there another way to accomplish what I'm trying to do? I'm aware of things like this to get the notebook name inside the notebook but that's not quite what I'm trying to do.
Windows 10,
Node v14.17.0,
npm 6.14.13,
jlpm 1.21.1,
jupyter lab 3.0.14
I'm using this example server extension as a template: https://github.com/jupyterlab/extension-examples/tree/master/server-extension
index.ts file from the existing extension to get the current tab name:
import {
ILabShell,
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '#jupyterlab/application';
import { Title, Widget } from '#lumino/widgets';
/**
* Initialization data for the jupyterlab-active-as-tab-name extension.
*/
const extension: JupyterFrontEndPlugin<void> = {
id: 'jupyterlab-active-as-tab-name',
autoStart: true,
requires: [ILabShell],
activate: (app: JupyterFrontEnd, labShell: ILabShell) => {
const onTitleChanged = (title: Title<Widget>) => {
console.log('the JupyterLab main application:', title);
document.title = title.label;
};
// Keep the session object on the status item up-to-date.
labShell.currentChanged.connect((_, change) => {
const { oldValue, newValue } = change;
// Clean up after the old value if it exists,
// listen for changes to the title of the activity
if (oldValue) {
oldValue.title.changed.disconnect(onTitleChanged);
}
if (newValue) {
newValue.title.changed.connect(onTitleChanged);
}
});
}
};
export default extension;
My index.ts file:
import {
ILabShell,
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '#jupyterlab/application';
import { ICommandPalette } from '#jupyterlab/apputils';
import { ILauncher } from '#jupyterlab/launcher';
import { requestAPI } from './handler';
import { ToolbarButton } from '#jupyterlab/apputils';
import { DocumentRegistry } from '#jupyterlab/docregistry';
import { INotebookModel, NotebookPanel } from '#jupyterlab/notebook';
import { IDisposable } from '#lumino/disposable';
import { Title, Widget } from '#lumino/widgets';
export class ButtonExtension implements DocumentRegistry.IWidgetExtension<NotebookPanel, INotebookModel> {
constructor(app: JupyterFrontEnd) {
this.app = app;
}
readonly app: JupyterFrontEnd
createNew(panel: NotebookPanel, context: DocumentRegistry.IContext<INotebookModel>): IDisposable {
// dummy json data to test post requests
const data2 = {"test message" : "message"}
const options = {
method: 'POST',
body: JSON.stringify(data2),
headers: {'Content-Type': 'application/json'
}
};
// Create the toolbar button
let mybutton = new ToolbarButton({
label: 'Measure Energy Usage',
onClick: async () => {
// POST request to Jupyter server
const dataToSend = { file: 'nbtest.ipynb' };
try {
const reply = await requestAPI<any>('hello', {
body: JSON.stringify(dataToSend),
method: 'POST'
});
console.log(reply);
} catch (reason) {
console.error(
`Error on POST /jlab-ext-example/hello ${dataToSend}.\n${reason}`
);
}
// sample POST request to svr.js
fetch('http://localhost:9898/api', options);
}
});
// Add the toolbar button to the notebook toolbar
panel.toolbar.insertItem(10, 'MeasureEnergyUsage', mybutton);
console.log("MeasEnerUsage activated");
// The ToolbarButton class implements `IDisposable`, so the
// button *is* the extension for the purposes of this method.
return mybutton;
}
}
/**
* Initialization data for the server-extension-example extension.
*/
const extension: JupyterFrontEndPlugin<void> = {
id: 'server-extension-example',
autoStart: true,
optional: [ILauncher],
requires: [ICommandPalette, ILabShell],
activate: async (
app: JupyterFrontEnd,
palette: ICommandPalette,
launcher: ILauncher | null,
labShell: ILabShell
) => {
console.log('JupyterLab extension server-extension-example is activated!');
const your_button = new ButtonExtension(app);
app.docRegistry.addWidgetExtension('Notebook', your_button);
// sample GET request to jupyter server
try {
const data = await requestAPI<any>('hello');
console.log(data);
} catch (reason) {
console.error(`Error on GET /jlab-ext-example/hello.\n${reason}`);
}
// get name of active tab
const onTitleChanged = (title: Title<Widget>) => {
console.log('the JupyterLab main application:', title);
document.title = title.label;
};
// Keep the session object on the status item up-to-date.
labShell.currentChanged.connect((_, change) => {
const { oldValue, newValue } = change;
// Clean up after the old value if it exists,
// listen for changes to the title of the activity
if (oldValue) {
oldValue.title.changed.disconnect(onTitleChanged);
}
if (newValue) {
newValue.title.changed.connect(onTitleChanged);
}
});
}
};
export default extension;
There are two ways to fix it and one way to improve it. I recommend using (2) and (3).
Your order of arguments in activate is wrong. There is no magic matching of argument types to signature function; instead arguments are passed in the order given in requires and then optional. This means that you will receive:
...[JupyterFrontEnd, ICommandPalette, ILabShell, ILauncher]
but what you are expecting is:
...[JupyterFrontEnd, ICommandPalette, ILauncher, ILabShell]
In other words, optionals are always at the end. There is no static type check so this is a common source of mistakes - just make sure you double check the order next time (or debug/console.log to see what you are getting).
Actually, don't require ILabShell as a token. Use the ILabShell that comes in app.shell instead. This way your extension will be also compatible with other frontends built using JupyterLab components.
shell = app.shell as ILabShell
(optional improvement) install RetroLab as a development-only requirement and use import type (this way it is not a runtime requirement) to ensure compatibility with RetroLab:
import type { IRetroShell } from '#retrolab/application';
// ... and then in `activate()`:
shell = app.shell as ILabShell | IRetroShell
to be clear: not doing so would not make your extension incompatible; what it does is ensures you do not make it incompatible by depending on lab-specific behaviour of the ILabShell in the future.
So in total it would look like:
import {
ILabShell,
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '#jupyterlab/application';
import type { IRetroShell } from '#retrolab/application';
// ...
const extension: JupyterFrontEndPlugin<void> = {
id: 'server-extension-example',
autoStart: true,
optional: [ILauncher],
requires: [ICommandPalette],
activate: async (
app: JupyterFrontEnd,
palette: ICommandPalette,
launcher: ILauncher | null
) => {
let shell = app.shell as ILabShell | IRetroShell ;
shell.currentChanged.connect((_, change) => {
console.log(change);
// ...
});
}
};
export default extension;

QT Quick Test KeyClicks

I am trying to implement a keyClick with a shift modifier but it doesn't work. Below is a basic setup of what I am trying to do. The first test_case1 can perform what I am doing but I'd like the second test_case2 to work as well but with using the Qt.ShiftModifier.
../MyTextBox.qml
Page {
id: page1
objectName: "page1"
TextField {
id: lastNameField
objectName: "lastNameField"
text: qsTr("")
}
}
tst_page.qml
import "../"
Item {
width: 800
height: 600
MyTextBox {
id: page1
}
TestCase {
id: "txtBox"
when: windowShown
function test_case1 () {
//var qmlObj = findChild(page1, "lastNameField")
var qmlObj = page1.lastNameField
// Bring to focus
mouseClick(qmlObj, Qt.LeftButton, Qt.NoModifier)
// Keypress
keyPress("Y")
keyPress("e")
keyPress("s")
tryCompare(qmlObj, "text", "Yes") // pass
}
function test_case2 () {
var qmlObj = page1.lastNameField
// Bring to focus
mouseClick(qmlObj, Qt.LeftButton, Qt.NoModifier)
// Keypress
keyClick(QT.Key_Y, Qt.ShiftModifier)
keyClick(QT.Key_E)
keyClick(QT.Key_S)
tryCompare(qmlObj, "text", "Yes") // fail
}
}
}
test output
PASS : test_case1()
FAIL! : test_case2()
Actual (): yes
Expected (): Yes
Edit: Added a simple project to github for testing.

Meteor methods cannot be added during run time when using typescript with SystemJs

I used meteor-typescript-compiler (https://github.com/meteor-typescript/meteor-typescript-compiler) for my new project. After setting up the project and getting the great help from #basarat, I was able to let the project startup normally. However, look like the generated js file is not triggered during the time server is executed, thus all the Meteor methods are not triggered and added to Meteor.
/// <reference path="../typings/definitions/meteor.d.ts" />
export class App {
constructor() {
}
}
Meteor.startup(function() {
console.log ('added to stack');
});
Meteor.methods({
'test': function() {
console.log('from new 2');
}
});
When starting up the meteor server, the generated js file is
(function(){
/////////////////////////////////////////////////////////////////////////
// //
// server/main.js //
// //
/////////////////////////////////////////////////////////////////////////
//
/// <reference path="../typings/definitions/meteor.d.ts" /> // 1
System.register("server/main", [], function(exports_1) { //
var App; //
return { //
setters:[], //
execute: function() { //
App = (function () { //
function App() { //
} //
return App; //
})(); //
exports_1("App", App); //
Meteor.startup(function () { //
console.log('added to stack'); //
}); //
Meteor.methods({ //
'test': function () { //
console.log('from new 2'); //
} //
}); //
} //
} //
}); //
//# sourceMappingURL=main.js.map //
/////////////////////////////////////////////////////////////////////////
}).call(this);
//# sourceMappingURL=main.js.map
And this is my tsconfig.json (I also added the .tsconfig to the root folder based on the meteor-typescript-compiler 's guidance)
{
"compilerOptions": {
"module": "commonjs",
"sourceMap": true,
"isolatedModules": false,
"noImplicitAny": true
},
"exclude": [
"typings"
]
}
The line added to stack is not shown when server is running (I added package systemjs:systemjs into meteor). For the testing purpose, I uploaded the project into https://github.com/bubuzzz/new1.git
thus all the Meteor methods are not triggered and added to Meteor.
You need to load the main.js file as a dependency. i.e someone needs to call the function that is registered against server/main (in System.register("server/main", [], function(exports_1) {)
Reading the docs : https://github.com/systemjs/systemjs#browser I seems you need
System.import('server/main.js');

How to remove rendered route using gmap3 plugin?

I use GMAP3 plugin to render driving direction. And would like to add a clear button so it can be clear but I haven't been able to find the right syntax in GMAP3. Here is the my js code, modified from the sample in gmap3.net. I have markers plotted already and latlng are retreived from plotted markers instead of from clicks position on the map.
function removePath() {
$(mapID).gmap3({
action: 'clear',
name: 'directionRenderer'
// tag: 'path' // works too with tag instead of name
});
function updatePath() {
$(mapID).gmap3({
action: 'getRoute',
options: {
origin: m1.getPosition(),
destination: m2.getPosition(),
travelMode: google.maps.DirectionsTravelMode.DRIVING
},
callback: function (results) {
if (!results) return;
$(mapID).gmap3({
action: 'setDirections',
directions:results,
});
}
});
};
function updateDirection(mm) { // Directions between m1 and m2
var mmID = $(mm).prop('id');
...
if (mmID == 'clearDirection') {
...
removePath();
return;
};
...
if (m1 && m2) { updatePath(); };
};
function initmap() {
$(mapID).gmap3(
{
action: 'init',
options: defaultMapOptions
},
// add direction renderer to configure options (else, automatically created with default options)
{ action: 'addDirectionsRenderer',
preserveViewport: true,
markerOptions: { visible: false },
options: {draggable:true},
tag: 'path'
},
// add a direction panel
{ action: 'setDirectionsPanel',
id: 'directions'
}
);
};
A is in place in HTML documents as directions panel. It has a a wrapper which is hidden when the route is cleared by using jquery css property change. The wrapper div's display property is changed back to 'block' whenever value is assigned to either m1 or m2.
<body>
...
<div id="direction_container" class="shadowSE">
....
<div id="directions"></div>
....
</div>
</body>
Its absolutely working fine.
$map.gmap3({ action: 'clear', name: 'directionRenderer' });
*Instructions-
If you later draw the route then you must write below code otherwise directions not display.
$map.gmap3({ action: 'addDirectionsRenderer', preserveViewport: true,
markerOptions: { visible: false} },
{ action: 'setDirectionsPanel', id: 'directions' });
Thanks...
Use this:
$(mapID).gmap3({action:"clear", name:"directionRenderer"});
The chosen answer above didn't work for me. I'm unsure if it's version related, but the solution I'm using is more simple:
$(your-selector).gmap3({clear: {}});
Afterwards, you can draw a new route without reconnecting the directions rendered with the map.

Resources