Using Blazor WebAssembly (3.2.0 preview 5) with BlazorLeaflet to show a map. On this map I want to put an imageOverlay that I want to be able to update more or less often (sometimes not at all and sometimes up to about 1-2 times per second). With a normal js this was quite easily done with the following code snippet (together with Leaflet.Overlay.Rotated plugin) :
Initially:
var map = L.map('mapElement', {
crs: crs,
continuousWorld: true,
center: latlong, // Set center location
zoom: 9, // Set zoom level
minzoom: 0,
maxzoom: 13
});
var overlay = L.imageOverlay.rotated(imageurl, point1, point2, point3, {
opacity: 0.4,
interactive: true,
attribution: "MyUpdatingImage"
});
map.addLayer(overlay);
Then updating the image is simply done by:
var ctx = canvas.getContext("2d");
var imageData = ctx.createImageData(width, height);
//..
// Populating imageData.data with values for r,g,b,a (0-255)
//..
overlay.setUrl(canvas.toDataURL());
I have briefly tested this Blazor Canvas plugin but unfortunately there is no toDataURL() equivalent method/function as far as I can see..
Is there anyone who knows of an alternative method in Blazor WebAssembly?
You can still do this, just use the IJSRuntime service to call a JavaScript method from Blazor WASM and make the update. Example:
#inject IJSRuntime JsRuntime
<canvas id="myMap"></canvas>
#code {
private async Task RefreshMap(){
await JsRuntime.InvokeVoidAsync("myJsMethod", "myNewMapData"); // send any data required
}
}
Related
i want to implement in my leaflet app, feature that allows to find route between two selected points.To find route i want to use this library: mapquest
I have extended standard leaflet map class like this:
export class Map {
constructor(elementId, centerView, zoom ) {
this.layers = [];
this.map = this.init(elementId,centerView,zoom);
this.icons = {};
}
init(elementId, centerView, zoom) {
//console.log('Map::init('+elementId+', ['+centerView+'], '+zoom+')');
delete L.Icon.Default.prototype._getIconUrl;
const markerIcon = require('leaflet/dist/images/marker-icon.png');
const markerIcon2x = require('leaflet/dist/images/marker-icon-2x.png');
const markerShadow = require('leaflet/dist/images/marker-shadow.png');
L.Icon.Default.mergeOptions({
iconRetinaUrl: markerIcon2x.default,
iconUrl: markerIcon.default,
shadowUrl: markerShadow.default,
});
var map = L.map(elementId, {
center: [centerView[0], centerView[1]],
zoom: zoom
});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
return map;
}
}
And i wrote few functions that helps me to work with map(add point, markers etc).
This is how i'm initializing extended map object:
let centerView = [35.791188, -78.636755];
let zoom = 9;
var map = new Map('map', centerView, zoom);
And i found code how to generate route mapquest-routing.I'am taking coords by clicking on map which works fine. My function to generate route looks like this:
function runDirection(start, end)
{
var dir = MQ.routing.directions();
dir.route({
locations: [
start,
end
]
});
map.map.addLayer(MQ.routing.routeLayer({
directions: dir,
fitBounds: true
}));
}
But i'm getting error:
Uncaught Error: The provided object is not a Layer.
Which means MQ.routing.routeLayer(),doesnt return me leyer object.
So the question is, how can i add route to standard leaflet map?
You're using a deprecated library. On the https://developer.mapquest.com/documentation/leaflet-plugins/geocoding/ webpage there's a deprecation warning in big red letters, I quote:
We recommend using MapQuest.js instead.
The documentation in the webpage for that mapquest plugin for Leaflet lists compatibility for Leaflet 0.7.7 (which was published back on 2013). Leaflet uses semantic versioning, which means stuff that worked with 0.x has no guarantee of working with 1.x. It's safe to assume that the Leaflet plugin in question only works with Leaflet 0.7.x and older, and does not work with Leaflet 1.x.
Background:
I'm working on integrating HERE maps with trucks restrictions to our web-based software solution.
Issue:
Halfway through the integration, I've noticed that when zooming-in/zooming-out the labels and truck restriction signs are blinking after reload. I've captured same behaviour in the example provided by HERE (link) themselves.
As the issue occurs in their own examples I think it is safe to state that it's not my implementation problem.
I've seen (also in their examples), that when their vector tiles are integrated using mapbox.gl, the zoom-in/zoom-out becomes smooth, but I would prefer to use their own javascript library due to other advantages (like traffic incidents display, etc.).
Question:
Anyone has any ideas how could that be solved?
I tried to reproduce the effect and i understand what you mean. From what i understand (i have not worked with HERE maps before), while a user zoom in/out the re-rendering process is the one that produces that single-flicker of a point sometimes.
There are 2 JSFiddle examples A and B, you will spot a difference but i think the problem not eliminated.
Example Code
/**
* A full list of available request parameters can be found in the Routing API documentation.
* see: http://developer.here.com/rest-apis/documentation/routing/topics/resource-calculate-route.html
*/
var routeRequestParams = {
routingMode: 'fastest',
transportMode: 'truck',
trafficMode: 'enabled',
origin: '40.7249546323,-74.0110042', // Manhattan
destination: '40.7324386599,-74.0341396'
},
routes = new H.map.Group();
async function calculateRoutes(platform) {
var router = await platform.getRoutingService(null, 8);
// The blue route showing a simple truck route
calculateRoute(router, routeRequestParams, {
strokeColor: 'rgba(0, 128, 255, 0.7)',
lineWidth: 10
});
// The green route showing a truck route with a trailer
calculateRoute(router, Object.assign(routeRequestParams, {
'truck[axleCount]': 4,
}), {
strokeColor: 'rgba(25, 150, 10, 0.7)',
lineWidth: 7
});
// The violet route showing a truck route with a trailer
calculateRoute(router, Object.assign(routeRequestParams, {
'truck[axleCount]': 5,
'truck[shippedHazardousGoods]': 'flammable'
}), {
strokeColor: 'rgba(255, 0, 255, 0.7)',
lineWidth: 5
});
}
/**
* Calculates and displays a route.
* #param {Object} params The Routing API request parameters
* #param {H.service.RoutingService} router The service stub for requesting the Routing API
* #param {mapsjs.map.SpatialStyle.Options} style The style of the route to display on the map
*/
async function calculateRoute (router, params, style) {
await router.calculateRoute(params, async function(result) {
await addRouteShapeToMap(style, result.routes[0]);
}, console.error);
}
/**
* Boilerplate map initialization code starts below:
*/
// set up containers for the map + panel
var mapContainer = document.getElementById('map');
// Step 1: initialize communication with the platform
// In your own code, replace variable window.apikey with your own apikey
var platform = new H.service.Platform({
apikey: window.apikey
});
var defaultLayers = platform.createDefaultLayers();
// Step 2: initialize a map - this map is centered over Berlin
var map = new H.Map(mapContainer,
// Set truck restriction layer as a base map
defaultLayers.vector.normal.truck,{
center: {lat: 40.745390, lng: -74.022917},
zoom: 13.2,
pixelRatio: window.devicePixelRatio || 1
});
// add a resize listener to make sure that the map occupies the whole container
window.addEventListener('resize', async () => await map.getViewPort().resize());
// Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
map.addObject(routes);
/**
* Creates a H.map.Polyline from the shape of the route and adds it to the map.
* #param {Object} route A route as received from the H.service.RoutingService
*/
function addRouteShapeToMap(style, route){
route.sections.forEach(async (section) => {
// decode LineString from the flexible polyline
let linestring = await H.geo.LineString.fromFlexiblePolyline(section.polyline);
// Create a polyline to display the route:
let polyline = await new H.map.Polyline(linestring, {
style: style
});
// Add the polyline to the map
await routes.addObject(polyline);
// And zoom to its bounding rectangle
await map.getViewModel().setLookAtData({
bounds: routes.getBoundingBox()
});
});
}
// Now use the map as required...
calculateRoutes (platform);
Unfortunately there isn't a good solution for this. It is caused by the way that the page is rendered through the here api. I don't think its fixable.
Having said that you might be able to add some css that would fade in the elements so the transition is smoother.
So you would need to detect the current value of
zoom:
and if it changes you would want to trigger the css animation.
It will take a bit of work and won't be a perfect solution but its better than nothing.
I have:
const map = L.map("mapid", {preferCanvas: true});
//....
const circle = L.circle([47.6652642, -122.3161248], {
color: '#ea647f',
fillOpacity: 0.4,
radius: 30
}).addTo(map);
but calling getBounds() on circle fails:
const bounds = circle.getBounds();
It fails inside function getBounds in Circle.js which is Leaflet code,
The Leaflet getBounds method code is:
getBounds: function () {
var half = [this._radius, this._radiusY || this._radius];
return new LatLngBounds(
this._map.layerPointToLatLng(this._point.subtract(half)),
this._map.layerPointToLatLng(this._point.add(half)));
}
Trying to access this._map.layerPointToLatLng fails
I get the error that this._map is undefined
Any ideas?
===================================================
Please Note: I also have a polygon defined,
and calling the getBounds() on the polygon passes fine and works correctly, shows correctly on the map.
=> It is only the Circle.getBounds() that fails
Add center and zoom to the map.
const map = L.map("map", {center:[47.6652642, -122.3161248], zoom: 16 ,preferCanvas: true});
You need to initialise the map state with setView() or some other mechanism. The layerPointToLatLng calls will fail otherwise.
I'm migrating from Google Maps API to Apple MapKit JS for the simple reason I have a developer account with them and they offer more free hits.
Anyway, actual examples of MapKit JS are a bit thin (or at least Google isn't finding them - draw what conspiracy theories you will), so although I've got the basics going of displaying an embeded map, I can't seem to do the next step which is route between two points (Apple's documentation also seems impenetrable as they don't show examples).
Here's my script for a basic map:
<script>
mapkit.init({
authorizationCallback: function(done) {
done('[MY-TOKEN]');
}
});
var MarkerAnnotation = mapkit.MarkerAnnotation
var myMarker = new mapkit.Coordinate(55.9496320, -3.1866360)
var myRegion = new mapkit.CoordinateRegion(
new mapkit.Coordinate(55.9496320, -3.1866360),
new mapkit.CoordinateSpan(0.003, 0.003)
);
var map = new mapkit.Map("map");
var myAnnotation = new MarkerAnnotation(myMarker, { color: "#9b6bcc", title: "theSpace On The Mile"});
map.showItems([myAnnotation]);
map.region = myRegion;
</script>
Now I want to:
• Show a walking route between two points
• Include waypoints on the route
Could someone show the code that would achieve this? Once I can see an example I know I'll get it ;-)
Ok, so I've found a solution to this so sharing it here for the benefit of others.
Let's start by saying Apple's MapKit JS doesn't appear to have a waypoints option as offered by Google Maps API - so the way around that is to create a map that stores the markers in an array and then routes from one to the next. The code stores the location of the last waypoint in a variable, and doesn't bother to draw a route to the last waypoint if this is the first one in the array (obviously).
<script>
// Initiallise MapKit - you'll need your own long-lived token for this
mapkit.init({
authorizationCallback: function(done) {
done('[MY-TOKEN]');
}
});
// Function to draw the route once MapKit has returned a response
function directionHandler(error, data) {
data["routes"].forEach(function(route, routeIdx) {
if (routeIdx !== 0) { return; }
overlays = [];
route['path'].forEach(function(path) {
// This styles the line drawn on the map
let overlayStyle = new mapkit.Style({
lineWidth: 3,
strokeColor: "#9b6bcc"
});
let overlay = new mapkit.PolylineOverlay(path, {
style: overlayStyle
});
overlays.push(overlay);
});
map.addOverlays(overlays);
});
}
// This asks MapKit for directions and when it gets a response sends it to directionHandler
function computeDirections(origin,destination) {
let directionsOptions = {
origin: origin,
destination: destination,
transportType: mapkit.Directions.Transport.Walking
};
directions.route(directionsOptions, directionHandler);
}
// This sets the initial region, but is overridden when all points have been potted to automatically set the bounds
var myRegion = new mapkit.CoordinateRegion(
new mapkit.Coordinate(55.9496320, -3.1866360),
new mapkit.CoordinateSpan(0.05, 0.05)
);
var map = new mapkit.Map("map");
map.region = myRegion;
var myAnnotations = [];
// lastWaypoint variable is 'unset' initially so the map doesn't try and find a route to the lastWaypoint for the first point of the route
var lastWaypoint = "unset";
var directions = new mapkit.Directions();
// Array of co-ordinates and label for marker
waypoints = [
{name:'Sofi’s Bar',lat:55.9746308,lon:-3.1722282},
{name:'TThe Roseleaf Cafe',lat:55.975992,lon:-3.173474},
{name:'Hemingway’s',lat:55.9763631,lon:-3.1706564},
{name:'Teuchter’s Landing',lat:55.9774693,lon:-3.1713826},
{name:'The King’s Wark',lat:55.9761425,lon:-3.1695419},
{name:'Malt and Hops',lat:55.975885,lon:-3.1698957},
{name:'The Carrier’s Quarters',lat:55.9760813,lon:-3.1685323},
{name:'Noble’s',lat:55.974905,lon:-3.16714},
{name:'The Fly Half',lat:55.9747906,lon:-3.1674496},
{name:'Port O’ Leith',lat:55.974596,lon:-3.167525}
];
// Loop through the array and create marker for each
waypoints.forEach(function(data) {
var myAnnotation = new mapkit.MarkerAnnotation(new mapkit.Coordinate(data['lat'],data['lon']), {
color: "#9b6bcc",
title: data['name']
});
myAnnotations.push(myAnnotation);
// As long as this isn't the first point on the route, draw a route back to the last point
if(lastWaypoint!="unset") {
computeDirections(lastWaypoint,new mapkit.Coordinate(data['lat'],data['lon']));
}
lastWaypoint = new mapkit.Coordinate(data['lat'],data['lon']);
});
map.showItems(myAnnotations);
</script>
This map is for a pub crawl around Leith, so the trasportType is 'Walking', but change that to 'Automobile' if you so wish.
With credit to Vasile whose MapKit JS Demo (https://github.com/vasile/mapkit-js-demo) helped me understand a lot more about the options.
So I have just figured out how to load a tilemap that I have created in the program tiled and it works as expected so then I am trying to add a player with a basic follow camera using some code from the example but I have nothing happening except the is viewing the middle of the tilemap now here is the code I have tried:
var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create });
function preload() {
// Tilemaps are split into two parts: The actual map data (usually stored in a CSV or JSON file)
// and the tileset/s used to render the map.
// Here we'll load the tilemap data. The first parameter is a unique key for the map data.
// The second is a URL to the JSON file the map data is stored in. This is actually optional, you can pass the JSON object as the 3rd
// parameter if you already have it loaded (maybe via a 3rd party source or pre-generated). In which case pass 'null' as the URL and
// the JSON object as the 3rd parameter.
// The final one tells Phaser the foramt of the map data, in this case it's a JSON file exported from the Tiled map editor.
// This could be Phaser.Tilemap.CSV too.
game.load.tilemap('gameMap', 'assets/tilemaps/gameMap2.json', null, Phaser.Tilemap.TILED_JSON);
// Next we load the tileset. This is just an image, loaded in via the normal way we load images:
game.load.image('tiles', 'assets/tilemaps/gameMap.png');
game.load.image('player','assets/img/player.gif');
}
var map;
var layer;
var player;
var cursors;
function create() {
game.stage.backgroundColor = '#787878';
game.world.setBounds(0, 0, 3200, 3200);
game.physics.startSystem(Phaser.Physics.P2JS);
player = game.add.sprite(game.world.centerX, game.world.centerY, 'player');
game.physics.p2.enable(player);
cursors = game.input.keyboard.createCursorKeys();
game.camera.follow(player);
// The 'mario' key here is the Loader key given in game.load.tilemap
map = game.add.tilemap('gameMap');
// The first parameter is the tileset name, as specified in the Tiled map editor (and in the tilemap json file)
// The second parameter maps this name to the Phaser.Cache key 'tiles'
map.addTilesetImage('spritesheet', 'tiles');
// Creates a layer from the World1 layer in the map data.
// A Layer is effectively like a Phaser.Sprite, so is added to the display list.
layer = map.createLayer('Tile Layer 1');
// This resizes the game world to match the layer dimensions
layer.resizeWorld();
}
function update() {
player.body.setZeroVelocity();
if (cursors.up.isDown)
{
player.body.moveUp(300)
}
else if (cursors.down.isDown)
{
player.body.moveDown(300);
}
if (cursors.left.isDown)
{
player.body.velocity.x = -300;
}
else if (cursors.right.isDown)
{
player.body.moveRight(300);
}
}
function render() {
game.debug.cameraInfo(game.camera, 32, 32);
game.debug.spriteCoords(player, 32, 500);
}
I am new to using phaser and I am not sure why this is not working any pointers?
Try to create the character's functionality and follow-up of the camera after having implemented the creation of the map in this way:
function create() {
//Configure physical engine
game.physics.startSystem(Phaser.Physics.P2JS);
//Creating the tilemap
game.stage.backgroundColor = '#787878';
map = game.add.tilemap('mario');
map.addTilesetImage('SuperMarioBros-World1-1', 'tiles');
layer = map.createLayer('World1');
layer.resizeWorld();
//Configure player controls
cursors = game.input.keyboard.createCursorKeys();
//Player creation
player = game.add.sprite(game.world.centerX, game.world.centerY, 'player');
game.physics.p2.enable(player);
game.camera.follow(player);
}
I have used this example as a basis: