I'm developing new web and I want to consume WMTS services with ArcGIS javascript API4
WMTSLayer in Javascript API 4.11 seems not working when spatialReference EPSG:25830 is selected.
The problem is that the request is generating by the API is incorrect, Tilerow parameter is wrong.
The request is being sent is this. http://www.ign.es/wmts/ign-base?SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetTile&LAYER=IGNBaseTodo&STYLE=default&FORMAT=image%2Fjpeg&TILEMATRIXSET=EPSG%3A25830&TILEMATRIX=18&TILEROW=236256&TILECOL=32268
And must be...
http://www.ign.es/wmts/ign-base?SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetTile&LAYER=IGNBaseTodo&STYLE=default&FORMAT=image%2Fjpeg&TILEMATRIXSET=EPSG%3A25830&TILEMATRIX=18&TILEROW=32374&TILECOL=32268
Here is my code example.
JSFIDDLE
or
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<link
rel="stylesheet"
href="https://js.arcgis.com/4.11/esri/themes/light/main.css"
/>
<title>Select WMTSLayer sublayer - 4.11</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 95%; /* allow space for sublayer switcher */
width: 100%;
}
select,
label {
font-family: "Avenir Next W00", "Helvetica Neue", Helvetica, Arial,
sans-serif;
}
#selectDiv {
background-color: lightgrey;
padding: 10px;
}
#theLabel {
visibility: hidden; /* hide until populated */
}
.esri-layer-list {
width: 310px;
}
</style>
<script src="https://js.arcgis.com/4.11/"></script>
<script>
var map, view;
require([
"esri/Map",
"esri/Basemap",
"esri/Viewpoint",
"esri/geometry/Extent",
"esri/views/MapView",
"esri/layers/WMTSLayer",
"esri/widgets/LayerList",
"esri/core/watchUtils"
], function(
Map,
Basemap,
Viewpoint,
Extent,
MapView,
WMTSLayer,
LayerList,
watchUtils
) {
var layerBase = {baseMapLayers: [{id: "basemap-layer", opacity: 1, title: "aaaaa", url: "http://arcgis.bizkaia.eus/arcgis/rest/services/ORTOFOTOS/GOBIERNO_VASCO_2016_AMPLIADO/MapServer", visibility: true, layerType:"ArcGISTiledMapServiceLayer"}], title: "Basemap"}
layer = new WMTSLayer({
url: "http://www.ign.es/wmts/ign-base"
});
map = new Map({
basemap: Basemap.fromJSON(layerBase),
});
view = new MapView({
container: "viewDiv",
map: map
});
view.when(function() {
layerList = new LayerList({
view: view
});
view.ui.add(layerList, "bottom-left");
layer.load();
layer.when(function() {
// add all sublayers to the select element
layer.sublayers.forEach(function(sublayer, i) {
if (sublayer.id === "IGNBaseOrto" || sublayer.id === "IGNBaseTodo" )
selectSublayer.options[ selectSublayer.options.length ] = new Option("(" + i + ") " + sublayer.title, sublayer.id);
});
// once populated, show the select element
var theDiv = document.getElementById("theLabel");
theDiv.style.visibility = "visible";
}); // end layer.load function
});
// if sublayer is changed, recreate map, view, and widgets using new activeLayer
document.getElementById("selectSublayer").onchange = function(event) {
map.removeAll();
if(event.target.value!==""){
layer.activeLayer = layer
.findSublayerById(event.target.value)
.clone();
map.layers.add(layer);
}
};
});
</script>
</head>
<body>
<div id="selectDiv">
<label id="theLabel">
Pick a layer from the WMTS catalog
<select id="selectSublayer"><option value="">No Layers Selected</option></select>
</label>
</div>
<div id="viewDiv"></div>
</body>
</html>
To reproduce the error one of the WMTSLayer must be selected from the select box.
There is a workaround to solve this problem?
Your basemap layer type is ArcGISTiledMapServiceLayer, which in the new API 4.11 is a TileLayer.
According to the documentation of the TileLayer from ESRI:
https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-TileLayer.html
Known Limitations
When adding a TileLayer to a map in a SceneView, the following
limitations exist:
This layer needs to be published from ArcGIS Server 10.3 and later or
ArcGIS Server 10.2.2 with this applied fix. If viewingMode is global,
then only services with ArcGIS Online/Bing Maps/Google Maps (Web
Mercator) or WGS84 Geographic Coordinate System, Version 2 tiling
scheme are supported. If viewingMode is local, then only services with
projected coordinate systems are supported. Only Tile layers with the
following tiling scheme specifications are supported:
256x256 or 512x512 pixel tiles Scale levels must increase or decrease
by a power of two At level 0 there shouldn't be more than 30 root
tiles. All tiled layers must have the same tiling scheme and
SpatialReference.
Please find the fiddle here and example which does not work but does not fail either (does not show 400 bad request when loading the layer)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<link
rel="stylesheet" href="https://js.arcgis.com/4.11/esri/themes/light/main.css"
/>
<title>Select WMTSLayer sublayer - 4.11</title>
<style>
html,body,
#viewDiv {
padding: 0;
margin: 0;
height: 95%; /* allow space for sublayer switcher */
width: 100%;
}
select, label {
font-family: "Avenir Next W00", "Helvetica Neue", Helvetica, Arial,
sans-serif;
}
#selectDiv {
background-color: lightgrey;
padding: 10px;
}
#theLabel {
visibility: hidden; /* hide until populated */
}
.esri-layer-list {
width: 310px;
}
</style>
<script src="https://js.arcgis.com/4.11/"></script>
<script>
var map, view, layer;
require([
"esri/Map",
"esri/Basemap",
"esri/views/MapView",
"esri/layers/WMTSLayer",
"esri/widgets/LayerList",
"esri/layers/TileLayer"
], function(Map,Basemap,MapView,WMTSLayer,LayerList,TileLayer) {
var basemap = new Basemap({
baseLayers: [
new TileLayer({
url: "http://arcgis.bizkaia.eus/arcgis/rest/services/ORTOFOTOS/GOBIERNO_VASCO_2016_AMPLIADO/MapServer"
})]
});
var layerBase = {
title: "Basemap",
baseMapLayers: [{
url: "http://arcgis.bizkaia.eus/arcgis/rest/services/ORTOFOTOS/GOBIERNO_VASCO_2016_AMPLIADO/MapServer",
visibility: true,
layerType:"ArcGISTiledMapServiceLayer"}]};
layer = new WMTSLayer({
url: "http://www.ign.es/wmts/ign-base"
});
map = new Map({
//basemap: Basemap.fromJSON(layerBase),
//basemap: "streets",
basemap: basemap,
layers: [layer]
});
view = new MapView({
container: "viewDiv",
map: map
});
view.when(function() {
view.extent = layer.fullExtent;
var layerList = new LayerList({
view: view
});
view.ui.add(layerList, "bottom-left");
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
I think the problem is in the reference system (which is readonly), the above layer is not re-projected to fit over the basemap.
In this image you can check what projections are supported on IGNBaseTodo layer the layer that I am trying to use.
I have load this layer on QGIS with EPSG:28530 spatial reference and I have captured the requests and compare with the requests that arcgis API4 is doing, the unique difference is tilerow parameter, everything else is ok.
image
Related
How to listen for different click events in mapbox using JavaScript? The following is the code for displaying a map using map box.
Can someone suggest how to listen to click events that differentiates land from water bodies?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Display a map on a webpage</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v2.2.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.2.0/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoiZGphZWJvIiwiYSI6ImNrbzJoYjhodTAxeDUyb211eTF2anZ0bmQifQ.62m9LNQhEPZNCwqxEW9pPQ';
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v11', // style URL
center: [-74.5, 40], // starting position [lng, lat]
zoom: 9 // starting zoom
});
</script>
</body>
</html>
I'm being able to listen to click events using the following code:
map.on('click', (e) => {
console.log(e.lngLat)
})
But is there a way to differentiate coordinates land from water bodies?
if you try this:
map.on('load', function () {
let style = map.getStyle();
})
then you can see all the details about the style such as sources, layers, glyphs and etc .
Also for listening to click events on different layers, you can just use:
map.on('click', 'land', function (e) {
// some code
});
or you can query the rendered features within multiple layers by code below:
map.on('click', function (e) {
let featureSelected = map.queryRenderedFeatures(e.point, {layers: ['land', 'water']
});
});
I have a map done on mapbox gl js.
I'm trying to get a placename / address by reverse geocoding longitude and latitude coordinates.
I can manage to get normal forward geocoding to work but querying the following (for example):
geocoder.query('New York')
But i cant quite figure out how to do this in reverse with coordinates. I've tried the following to no avail:
// geocoder.query(126.981,37.539)
// geocoder.query("126.981,37.539")
// geocoder.query(37.539,126.981)
// geocoder.query("37.539,126.981")
I couldnt find anything in the documents to point me in the right direction as well. Thus any help is greatly appreciated.
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title>Set a point after Geocoder result</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.49.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.49.0/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
.built_address {
position: fixed;
top: 0;
left: 0;
background-color: white;
padding: 10px;
font-size: 12px;
font-family: Arial;
}
</style>
</head>
<body>
<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v2.3.0/mapbox-gl-geocoder.min.js'></script>
<link rel='stylesheet' href='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v2.3.0/mapbox-gl-geocoder.css' type='text/css' />
<style>
#geocoder-container > div {
min-width:50%;
margin-left:25%;
}
</style>
<div id='map'></div>
<div class="built_address">LOREM IPSUM</div>
<script>
var built_address = '';
var user_coordinates;
var geocode_results;
mapboxgl.accessToken = 'pk.eyJ1IjoiZmFyaXNrYXNzaW0iLCJhIjoiSk1MaUthdyJ9.vkxtdDbYdLi524WwlKORBw';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/fariskassim/cjmszx78b266o2rlar02ytynj',
center: [127.017768, 37.59837],
zoom: 12
});
var geocoder = new MapboxGeocoder({
accessToken: mapboxgl.accessToken
});
map.addControl(geocoder);
// After the map style has loaded on the page, add a source layer and default
// styling for a single point.
map.on('load', function() {
map.addSource('single-point', {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": []
}
});
map.addLayer({
"id": "point",
"source": "single-point",
"type": "circle",
"paint": {
"circle-radius": 10,
"circle-color": "#007cbf"
}
});
getUserLocation();
});
function getUserLocation() {
// request to allow user position
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
function showPosition(position) {
// get user current coordinates and center map on coordiates
console.log('L2', position)
//console.log(position.coords.latitude, position.coords.latitude)
user_coordinates = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
// draw user location on mao
map.getSource('single-point').setData({type: "Point", coordinates: [user_coordinates.lng,user_coordinates.lat]});
// geocoder.query(user_coordinates.lat, user_coordinates.lng)
// Listen for the `result` event from the MapboxGeocoder that is triggered when a user
// makes a selection and add a symbol that matches the result.
geocoder.on('result', function(ev) {
map.getSource('single-point').setData(ev.result.geometry);
console.log('ev',ev)
built_address = ev.result.place_name
});
}
} else {
// if device doesnt support location
console.log('device doesnt support location')
}
}; /* END getUserLocation(); */
</script>
</body>
</html>
Try geocoder.mapboxClient.geocodeReverse:
geocoder.mapboxClient
.geocodeReverse({
latitude: user_coordinates.lat,
longitude: user_coordinates.lng
}, function(err, res) {
console.log(err, res)
});
Update
mapbox-gl-geocoder v5.0.0
You have to create a geocoder with reverse geocoding enabled :
let geocoder = new MapboxGeocoder({
accessToken:mapboxgl.accessToken,
mapboxgl:mapboxgl,
reverseGeocode:true
})
Then you will be able to do :
geocoder.query("126.981,37.539")
...
Be aware that the default coordinates format is lat,lng but you can invert it with the options.flipCoordinates
Looked over the example can NOT figure out how to set basemap MANUALLY. I don't want a dijit widget, or any other libraries or anything like that. Just want to manually set a basemap to any of the already available types like Topographic, Satellites, Streets, etc.
Following this API reference:
Object: esri/basemaps
The part I can't figure out is marked with question marks. If some could help me out, would really appreciate it.
require([
"esri/basemaps",
"esri/map",
"dojo/domReady!"
], function (esriBasemaps, Map) {
/* ------------------------------------- */
/* Basemap add one of the existing maps. */
/* ------------------------------------- */
esriBasemaps.myBasemap = {
baseMapLayers ???
};
var map = new Map("map", {
basemap: "myBasemap",
center: [-118, 34.5],
zoom: 8
});
});
The code in the esri/basemaps documentation works fine, combined with the create a map sample.
Here's the part you wondered about:
esriBasemaps.myBasemap = {
baseMapLayers: [
{
url: "http://services.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer"
}
],
title: "My Basemap"
};
Here's a full example. Copy and paste the following into the ArcGIS API for JavaScript Sandbox to see how it works.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
<title>Simple Map</title>
<link rel="stylesheet" href="http://js.arcgis.com/3.13/esri/css/esri.css">
<style>
html, body, #map {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
body {
background-color: #FFF;
overflow: hidden;
font-family: "Trebuchet MS";
}
</style>
<script src="http://js.arcgis.com/3.13/"></script>
<script>
var map;
require(["esri/basemaps", "esri/map", "dojo/domReady!"], function(esriBasemaps, Map) {
esriBasemaps.myBasemap = {
baseMapLayers: [
{
url: "http://services.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer"
}
],
title: "My Basemap"
};
map = new Map("map", {
basemap: "myBasemap",
center: [-122.45, 37.75], // longitude, latitude
zoom: 13
});
});
</script>
</head>
<body>
<div id="map"></div>
</body>
</html>
I am trying to reproduce the esri/dijit/Search tutorial that is new in the 3.13 release of the ArcGIS API for JavaScript with one of my own layers.
Esri Sample
<!DOCTYPE html>
<html><link rel="stylesheet" href="http://js.arcgis.com/3.13/esri/css/esri.css">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no" />
<title>ArcGIS API for JavaScript | Search widget with multiple sources</title>
<link rel="stylesheet" href="http://js.arcgis.com/3.13/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.13/esri/css/esri.css">
<style>
html,
body,
#map {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
#search {
display: block;
position: absolute;
z-index: 2;
top: 20px;
left: 74px;
}
</style>
<script src="http://js.arcgis.com/3.13/"></script>
<script>
require([
"esri/map", "esri/dijit/Search", "esri/layers/FeatureLayer", "esri/InfoTemplate", "esri/SpatialReference", "esri/geometry/Extent", "dojo/domReady!"
], function (Map, Search, FeatureLayer, InfoTemplate, SpatialReference, Extent) {
var map = new Map("map", {
basemap: "gray",
center: [-97, 38], // lon, lat
zoom: 5
});
var s = new Search({
enableButtonMode: true, //this enables the search widget to display as a single button
enableLabel: false,
enableInfoWindow: true,
showInfoWindowOnSelect: false,
map: map
}, "search");
var sources = [];
sources.push({
featureLayer: new FeatureLayer("http://maps.eastriding.gov.uk/arcgis/rest/services/GISIntranet/MapServer/0"),
searchFields: ["ADDRESS_WITHOUT_BREAKS"],
displayField: "ADDRESS_WITHOUT_BREAKS",
exactMatch: false,
name: "ADDRESS_WITHOUT_BREAKS",
outFields: ["*"],
placeholder: "ADDRESS_WITHOUT_BREAKS",
maxResults: 6,
maxSuggestions: 6,
enableSuggestions: true,
minCharacters: 0
});
//Set the sources above to the search widget
s.set("sources", sources);
s.startup();
});
</script>
</head>
<body>
<div id="search"></div>
<div id="map"></div>
</body>
</html>
I am having with the suggestions part if it. If I use the default source for this it will for fine. I will enter the first few character in the textbox and it will produce a number of suggestion that I can choose from. When I use my own source (as above) there is no suggestions that appear. I have check for errors and there are none. I have also checked the network in my Dev tools and the suggestions part is not firing off a query like the default source does.
I am wondering if I need something special setting up on my layer or should it just work
Thanks
From the API documentation
Working with suggestions is only available if working with a 10.3 geocoding service that has the suggest capability loaded or a 10.3 feature layer that supports pagination, i.e. supportsPagination = true.
Looking at your server you are running 10.2.2 so you need to move to 10.3 and make sure you allow pagination for your service in order to get this capability.
Checked these out here:
Google Maps API V3 multiple markers with links
Google Maps marker as a link
but they didn't help. I'm not programming savvy and total n00b. I'm simply trying to add some marker animations to my webpage. I'd like to be able to click on an individual marker once it settles and be redirected to that associated marker url.
Update: thank you so much for following up on this. However I get a blank screen when I try to run the html page. I updated my code below. I also removed the z index and put all your code updates between the tags inside the tag) Finally, I removed the intialize(); call function and instead used the . Running the code should theoretically drop 3 markers onto the map with the designated urls given the neighborhoods.length = 3. However the page still remains blank after refreshing.
So something is blocking the browser from loading the map onto the page. Does the map-canvas need to be adjusted in the ? My updated code is in next post below: –
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Marker animations with <code>setTimeout()</code></title>
<style>
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
#panel {
position: absolute;
top: 5px;
left: 50%;
margin-left: -180px;
background-color: #fff;
padding: 5px;
border: 1px solid #999;
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"
type="text/javascript"></script>
</head>
<body onload="initialize()";>
<script>
<div id="panel" style="margin-left: -52px">
<button id="drop" onclick="drop()">Drop Markers</button>
</div>
<div id="map-canvas"></div>
var sacramento = new google.maps.LatLng(38.576725, -121.493715);
var iterator = 0; // I added back this initialized variable which //wasn't in your adjusted code shown here
var neighborhoods = [
new google.maps.LatLng(38.576725, -121.493715),
new google.maps.LatLng(35.011263, -115.473376),
new google.maps.LatLng(33.941820, -118.408466)];
var markers = [];
var map;
var mapUrl = [
'http://www.google.com',
'http://www.youtube.com',
'http://maps.google.com'];
function initialize() {
{......etc. }
</script>
</body>
</html>
You have several issues in your code. Syntax, etc. I have removed a few things from your code to make it easier to understand (setTimeout, toggleBounce, etc.). And I added the call to drop() function in the initialize() function. I also replaced the window.location.href by a display of the URL in a DIV. Check the fiddle link at the bottom.
var sacramento = new google.maps.LatLng(38.576725, -121.493715);
var neighborhoods = [
new google.maps.LatLng(38.576725, -121.493715),
new google.maps.LatLng(35.011263, -115.473376),
new google.maps.LatLng(33.941820, -118.408466)];
var markers = [];
var map;
var mapUrl = [
'http://www.google.com',
'http://www.youtube.com',
'http://maps.google.com'];
function initialize() {
var mapOptions = {
zoom: 5,
center: sacramento
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
drop();
}
function drop() {
for (var i = 0; i < neighborhoods.length; i++) {
addMarker(i);
}
}
function addMarker(iterator) {
var marker = new google.maps.Marker({
position: neighborhoods[iterator],
map: map,
draggable: false,
url: mapUrl[iterator],
animation: google.maps.Animation.DROP
});
google.maps.event.addListener(marker, 'click', function () {
document.getElementById('goToLocation').innerHTML = 'Will send you to ' + this.url;
});
markers.push(marker);
}
initialize();
This should help you to get started.
JSFiddle