I have been browsing the HERE Maps API for Javascript docs for a while now and found no information whther it was possible to use custom tiles from Tilestache in HERE Maps API for Javascript.
My question to you who are more experienced with this API than me: Is it possible to use custom tiles at all in HERE Maps API for Javascript?
Many thanks in advance!
It's possible to use custom map tiles with here maps. You can find an example of how to do it here:
https://developer.here.com/api-explorer/maps-js/v3.0/infoBubbles/custom-tile-overlay
I recommend checking the full example, but in any case the key points are these:
1) create a tile provider and specify the url format
var tileProvider = new H.map.provider.ImageTileProvider({
// We have tiles only for zoom levels 12–15,
// so on all other zoom levels only base map will be visible
min: 12,
max: 15,
getURL: function (column, row, zoom) {
... omitted
// The Old Berlin Map Tiler follows the TMS URL specification.
// By specification, tiles should be accessible in the following format:
// http://server_address/zoom_level/x/y.png
return 'tiles/'+ zoom+ '/'+ row + '/'+ column+ '.png';
}
}
});
2) Create a layer and add it to the map
// Now let's create a layer that will consume tiles from our provider
var overlayLayer = new H.map.layer.TileLayer(tileProvider, {
// Let's make it semi-transparent
opacity: 0.5
});
// Finally add our layer containing old Berlin to a map
map.addLayer(overlayLayer);
Related
Background:
I'm currently integrating HERE maps into our web-based application. I'm trying both - HERE provided Javascript API and Leaflet at the same time to find the best approach for our use-case.
While JavaScript API provided by HERE maps is OK, rendering wise Leaflet performs much better when using raster tiles.
Issue:
It would be fine by me to use raster tiles + leaflet, but our application also needs to display traffic incidents data.
Traffic incident data is provided by HERE in JSON and XML formats (Documentation link, Example JSON). They provide [Z]/[X]/[Y], quadkey, prox, bbox, or corridor filters which can be used to retrieve filtered data set.
I've tried using [Z]/[X]/[Y] addressing with custom L.TileLayer implementation which loads appropriate JSON, converts it to GeoJSON and displays GeoJSON on map. However that approach is very inefficient and significant performance drop is visible.
Question:
Maybe anyone has already solved this issue and could share any insights on how the HERE traffic incidents could be shown on Leaflet map without encountering performance issues?
I created the following script, which works without any performance issues:
var fg = L.featureGroup().addTo(map);
function loadTraffic(data) {
fg.clearLayers();
var d = data.TRAFFICITEMS.TRAFFICITEM.map((r) => {
var latlngs = [];
if (r.LOCATION.GEOLOC) {
if (r.LOCATION.GEOLOC.ORIGIN) {
latlngs.push(L.latLng(r.LOCATION.GEOLOC.ORIGIN.LATITUDE, r.LOCATION.GEOLOC.ORIGIN.LONGITUDE));
}
if (r.LOCATION.GEOLOC.TO) {
if (L.Util.isArray(r.LOCATION.GEOLOC.TO)) {
r.LOCATION.GEOLOC.TO.forEach((latlng) => {
latlngs.push(L.latLng(latlng.LATITUDE, latlng.LONGITUDE));
})
} else {
latlngs.push(L.latLng(r.LOCATION.GEOLOC.TO.LATITUDE, r.LOCATION.GEOLOC.TO.LONGITUDE));
}
}
}
var desc = r.TRAFFICITEMDESCRIPTION.find(x => x.TYPE === "short_desc").content;
return {
latlngs,
desc
}
})
console.log(d);
d.forEach((road)=>{
L.polyline(road.latlngs,{color: 'red'}).addTo(fg).bindPopup(road.desc);
});
map.fitBounds(fg.getBounds())
}
If this script is not working for you, please share your json file.
Ok, so I've found a solution for this task. Apparently I was on a good path, I only needed to optimize my implementation.
What I had to do to achieve appropriate performance is:
Create custom CircleMarker extension which would draw custom icon on canvas
Create JS worker which would fetch the data from a given URL, transform it to GeoJSON and return GeoJSON to it's listener
Create custom GridLayer implementation, which, in fetchTile function, creates worker instance, passes it a link with appropriate [Z]/[X]/[Y] coordinates already set, adds listener, which listens for worker's done event and returns empty tile
On worker's done event, custom GridLayer implementation creates GeoJSON layer, adds it to the dictionary with coordinates as a key and, if zoom level is still the same - adds that layer to the map
Add zoomend observer on a map, which removes any layers that does not match current zoom level from the map
Now the map is definitely usable and works way faster than original HERE JS API.
P.S. Sorry, but I can't share the implementation itself due to our company policies.
I have a bokeh Google maps plot with several Lat/ Lng data points, and I would like to use the fitBounds() method of the Google Maps v3 API to set the zoom level.
I have a Google Maps plot up and running, displaying on my site and showing the data points, but I need to set the zoom manually.
import bokeh.io
import bokeh.models
import bokeh.plotting
import pandas as pd
data = {
'Latitude [deg]': [18.46, 25.7, 32.3],
'Longitude [deg]': [-66, -80.2, -64.8],
}
data_frame = pd.DataFrame.from_dict(data)
data_source = bokeh.models.ColumnDataSource(data_frame)
mean_lat = data_frame['Latitude [deg]'].mean()
mean_lng = data_frame['Longitude [deg]'].mean()
gmap_options = bokeh.models.GMapOptions(lat=mean_lat, lng=mean_lng, zoom=10, map_type='satellite')
xy_gmap = bokeh.plotting.gmap('SuPeRSeCrEtAPIkey', gmap_options)
xy_gmap.circle(x='Longitude [deg]', y='Latitude [deg]', source=data_source, color="red")
# A callback like this? Could make the call in JavaScript after the page is loaded, and update map zoom??
# xy_gmap.fitBounds(x='Longitude [deg]', y='Latitude [deg]', source=data_source)
bokeh.io.show(xy_gmap)
I would like the bounds of the map to enclose all points in my dataframe, at the lowest zoom possible (as is done in Javascript by fitBounds() ). Currently, the map can only zoom to the manually set level.
As of Bokeh 1.2, there is no built-in way to accomplish this. The only suggestion I can offer is to note that there is a global Bokeh.index on the JavaScript side, and in that there is GMapPlotView for the corresponding GmapPlot that you made. This view has an attribute .map that is the actual Google map object. You could call the fitBounds method on that from JavaScript code. Bokeh GMapPlot objects follow Google's lead wrt to plot bounds, so if they get updated, the axes, etc. should respond.
Otherwise, I can only suggest opening a GitHub issue to discuss adding this as a new feature.
Starting an answer for both methods described by bigreddot (using JS Bokeh.index and Python integration)
Bokeh.index
In the Javascript Console, you can use the following commands (should be self explanatory to get this into some JS code)
> var gmapPlotView;
> for (var property in Bokeh.index) { if (Bokeh.index[property].constructor.name == 'GMapPlotView') {gmapPlotView = Bokeh.index[property];} } // Get the correct object, in the case that you have multiple plots, but only one map plot
> var bounds = gmapPlotView.map.getBounds(); // To start from existing bounds
> var bounds = google.maps.LatLngBounds(); // To start with fresh bounds
> var place = new google.maps.LatLng(45.5983128 ,8.9172776); // Coordinates of place to put bounds, repeat for all points
> bounds.extend(place); // Add each place to the bounds object
> gmapPlotView.map.fitBounds(bounds); // Set the maps new bounds
Note that the Bokeh google maps implementation does not plot your data on the Google Maps data layer, but on a bokeh canvas above the map (that is why there is a lag when panning the map between map tiles and your graphs).
Python Integration
In process....
I'm a learner of JavaScript and have a problem with Mapbox GL JS: I have a style in Mapbox Studio, where there is one my layer — "locations". I've added it as a tileset. There are two GeoJSON-points in this layer, but I can't get them in GL JS.
I've found that I should use method querySourceFeatures(sourceID, [parameters]), but I have problems with correct filling its parameters. I wrote:
var allFeatures = map.querySourceFeatures('_id-of-my-tyleset_', {
'sourceLayer': 'locations'
});
..and it doesn't work.
More interesting, that later in the code I use this layer with method queryRenderedFeatures, and it's okay:
map.on('click', function(e) {
var features = map.queryRenderedFeatures(e.point, {
layers: ['locations']
});
if (!features.length) {
return;
}
var feature = features[0];
flyToPoint(feature);
var popup = new mapboxgl.Popup({
offset: [0, -15]
})
.setLngLat(feature.geometry.coordinates)
.setHTML('<h3>' + feature.properties.name + '</h3>' + '<p>' +
feature.properties.description + '</p>')
.addTo(map);
});
I have read a lot about adding layers on a map and know that the answer is easy, but I can't realise the solution, so help, please :)
Here is the project on GitHub.
Your problem is that your map, like all maps created in Mapbox Studio by default, uses auto-compositing. You don't actually have a source called morganvolter.cj77n1jkq1ale33jw0g9haxc0-2haga, you have a source called composite with many sub layers.
You can find the list of layers like this:
map.getSource('composite').vectorLayerIds
Which reveals you have a vector layer called akkerman. ("locations" is the name of your style layer, not your source layer). Hence your query should be:
map.querySourceFeatures('composite', {
'sourceLayer': 'akkerman'
});
Which returns 4 features.
There are lots of questions about Mapbox get features after filter or Mapbox get features before filter. And I could see there are many posts are scattering around but none of them seem to have a FULL DETAILED solution. I spend some time and put both solution together under a function, try this in jsbin.
Here it is for someone interested:
function buildRenderedFeatures(map) {
// get source from a layer, `mapLayerId` == your layer id in Mapbox Studio
var compositeSource = map.getLayer(mapLayerId.toString()).source;
//console.log(map.getSource(compositeSource).vectorLayers);
var compositeVectorLayerLength = map.getSource(compositeSource).vectorLayers.length - 1;
//console.log(compositeVectorLayerLength);
// sourceId === tileset id which is known as vector layer id
var sourceId = map.getSource(compositeSource).vectorLayers[compositeVectorLayerLength].id;
//console.log(sourceId);
// get all applied filters if any, this will return an array
var appliedFilters = map.getFilter(mapLayerId.toString());
//console.log(appliedFilters);
// if you want to get all features with/without any filters
// remember if no filters applied it will show all features
// so having `filter` does not harm at all
//resultFeatures = map.querySourceFeatures(compositeSource, {sourceLayer: sourceId, filter: appliedFilters});
var resultFeatures = null;
// this fixes issues: queryRenderedFeatures getting previous features
// a timeout helps to get the updated features after filter is applied
// Mapbox documentation doesn't talk about this!
setTimeout(function() {
resultFeatures = map.queryRenderedFeatures({layers: [mapLayerId.toString()]});
//console.log(resultFeatures);
}, 500);
}
Then you call that function like: buildRenderedFeatures(map) passing the map object which you already have when you created the Mapbox map.
You will then have resultFeatures will return an object which can be iterated using for...in. You can test the querySourceFeatures() code which I commented out but left for if anyone needs it.
I'm developing a web mapping site (using google maps api 3.0 and javascript) that combines wms layers and ground overlays (uploaded rasters) that are displayed on top of google maps. The software is working well except that I'm having problems controlling the draw order of the layers. I would like to have the wms layer (the NRCS soils layer) displayed on top of the custom raster images, with the google maps as the base layer. Currently, the wms layer displays as expected on top of the google maps layer, but is covered by the raster layer. The question is: does the google maps api allow control of the order that layers are displayed (in my case the vector layer on top of raster layer on top of google maps)? I have tried setting the zindex of the display order, but that has not worked (but I could easily be missing something).
WMS layers are also rasters, not vectors -- I assume you are using an ImageMapType along with your GroundOverlay?
Leaving that aside, there is currently no way to control the layer ordering once they have been added to the map.
As a hack (untested) you may wish to add the layers in the order you wish them to be drawn, with some timeout between... I think this may work (again, untested).
(optional).
how i order it.
first i create layer in object like this.
layer = {};
layer.nrcs_soils= new google.maps.ImageMapType({
getTileUrl: function (coord, zoom) {
return getTileWmsUrl(coord, zoom, "nrcs_soils");
},
tileSize: new google.maps.Size(256, 256),
opacity: 1,
name:'nrcs_soils',
alt:{
layer_name:'nrcs_soils'
,order : 0
},
isPng: true
});
after that i create simple function add layer to map.
add_layer = function(layer_name){
//-- get order from object layer
var order_layer =layer[layer_name].alt.order;
map.overlayMapTypes.insertAt(order_layer ,layer[layer_name]);
}
just call this function for add and order layer from your setting on your object layer.
add_layer("nrcs_soils");
I'm trying to create map (using the Google Maps JavaScript API V3) which consists of several partially-transparent layers. By default, these layers should all be overlaid on top of one another to form a complete map, but the user should be able to turn any combination of them on or off (while preserving order) to create whatever view they prefer.
So far, I've had a great deal of luck getting this working for a single layer using map.mapTypes, but when adding all the layers via map.overlayMapTypes, I've hit a couple of snags:
The map doesn't seem to get fully initialized if map.setMapTypeId() is not called (no controls appear and the map is not correctly centered) and it cannot be called with an overlay.
It isn't clear how to toggle the visibility of an overlay without directly modifying the map.overlayMapTypes array, which complicates keeping them correctly ordered. I'd much prefer something analogous to the Traffic/Transit/Photos/etc. control available within Google Maps itself.
Here's the initialize function I'm working with. I'd post a link, but the map imagery isn't publicly available:
function initialize() {
map = new google.maps.Map(document.getElementById("map_canvas"), {
zoom: 0,
center: center
});
/* if these lines are uncommented, the single layer displays perfectly */
//map.mapTypes.set("Layer 3", layers[3]);
//map.setMapTypeId("Layer 3");
//return;
var dummy = new google.maps.ImageMapType({
name: "Dummy",
minZoom: 0,
maxZoom: 6,
tileSize: new google.maps.Size(256, 256),
getTileUrl: function() {return null; }
});
map.mapTypes.set("Dummy", dummy);
map.setMapTypeId("Dummy");
// layers is an array of ImageMapTypes
for (var i = 0; i < layers.length; i++) {
map.overlayMapTypes.push(layers[i]);
}
}
As you can see, I've tried creating a "dummy" maptype (which always returns null for tile URLs) to serve as the base map. While this does cause the controls to display, it still doesn't center correctly.
What's the best way to create a map which consists only of toggleable overlays?
Update: Turns out the dummy maptype works perfectly well if you also remember to set a projection. That's one problem solved, at least. :-)
I use ImageMapType, but I don't add it to mapTypes. I just add it to overlayMapTypes and when I need to remove it I use setAt to set the entry in overlayMapTypes to null.
You will need to add individual controls to the UI that toggle the individual layers.