Using the code below, I've been unable to get my single GeoJSON point feature to appear on the map. Or possibly if it does appear on the map in the correct location. It appears in Germany, whereas it should appear in Tasmania, Australia. Same location it appears at if I provide no projection at all. Therefore it seems that there is some problem with the way I'm setting the projection.
I have a feeling that I'm using defaultDataProjection and/or featureProjection incorrectly, but I find the documentation to be fairly unclear on how these should be used.
The same projection works on my other (non-GeoJSON) layers OK.
How can I correct my code to get this to work?
var geojsonObject = {"type":"FeatureCollection","features":[{"geometry":{"type":"Point","coordinates":[503619.026899999939,5420925.347199999727]},"properties":{"label":{}},"type":"Feature"}]};
proj4.defs('layerProj', '+proj=utm +zone=55 +south +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs ')
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON({
defaultDataProjection: 'layerProj',
}).readFeatures(geojsonObject, { featureProjection: 'layerProj' }))
})
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: new ol.style.Style({image: new ol.style.Circle({fill: new ol.style.Fill({color: '#8888FF'}), radius: 10, points: 0})}),
});
map.addLayer(vectorLayer);
Your format: should be features: Also defaultDataProjection in the GeoJSON options in OpenLayers 4 changes to dataProjection in later version, so it may be better to specify dataProjection in the readFeatures options where the name is the same in all versions. featureProjection should always be set to the view projection
features: new ol.format.GeoJSON().readFeatures(geojsonObject, {
dataProjection: 'layerProj',
featureProjection: map.getView().getProjection()
})
If you are using OpenLayers 5 or above you will also need to register proj4 after the layerproj definition
ol.proj.proj4.register(proj4);
Related
I want to load a country specific map (say https://openstreetmap.in). I'm using the following code snippet, but it loads the map from https://www.openstreetmap.org:
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
]
Can anybody please tell me how I can load a country specific map?
You need to create a custom tile layer, either a so-called OSM layer or a XYZ layer. The OpenLayer examples Localized OpenStreetMap and XYZ explain how to specify a custom tile source.
The tile server URL for openstreetmap.in is https://{a-c}.tiles.mapbox.com/v4/openstreetmap.1b68f018/{z}/{x}/{y}#2x.png?access_token=pk.eyJ1IjoiamluYWxmb2ZsaWEiLCJhIjoiY2psejFtZG8wMWhnMjNwcGFqdTNjaGF2MCJ9.ZQVAZAw8Xtg4H2YSuG4PlA.
Your code should then look roughly like this:
var osmIndia = new TileLayer({
source: new OSM({
attributions: [
'© mapbox and OpenStreetMap'
],
url: 'https://{a-c}.tiles.mapbox.com/v4/openstreetmap.1b68f018/{z}/{x}/{y}#2x.png?access_token=pk.eyJ1IjoiamluYWxmb2ZsaWEiLCJhIjoiY2psejFtZG8wMWhnMjNwcGFqdTNjaGF2MCJ9.ZQVAZAw8Xtg4H2YSuG4PlA'
})
});
[...]
layers: [
osmIndia
]
I'm trying to build a map for the game Guild Wars 2, but having trouble getting openlayers to handle the coordinate system correctly and render the map completely.
Here's a fiddle of what I have: https://jsfiddle.net/ndqb8rqx/
The Guild Wars world is square and 49152 pixels on both axes. The origin ([0, 0]) of the coordinates should be the north-west. The south-east should be [49152, 49152]. The developer of the game makes the tiles for the map available as a service: https://wiki.guildwars2.com/wiki/API:Tile_service
I created a Projection based on Zoomify to try to handle this:
var gw2Projection = new ol.proj.Projection({
code: 'ZOOMIFY',
units: 'pixels',
extent: [0, -49152, 49152, 0]
})
And I also added the game tiles as a Tilelayer from an XYZ source, like this:
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://tiles.guildwars2.com/1/1/{z}/{x}/{y}.jpg',
projection: gw2Projection
}),
}),
The first problem is that openlayers doesn't render the complete map. The map actually extends further south and east than the fiddle shows. The bottom right edge which is actually shown in the fiddle is really [32768, 32768], but the map extends out to [49152, 49152]. It seems that openlayers simply shrinks the coordinate system to the area of a single tile when zoomed completely out. Instead I would like the coordinates to match 1:1 with the pixels.
The other problem is that currently the y-coordinate increase in the northern direction. I would like to invert the y-axis, such that the y-coordinates increase in the southern direction.
#ahocevar nudged me in the right direction. The suggestion of creating a custom tilegrid was necessary but instead of setting the zoom level, it was necessary to set the actual resolutions.
So by adding a tilegrid like this:
var tilegrid = new ol.tilegrid.TileGrid({
extent: extent,
resolutions: [128, 64, 32, 16, 8, 4, 2, 1]
});
Using it in the layer source like this:
source: new ol.source.XYZ({
tileGrid: tilegrid,
url: 'https://tiles.guildwars2.com/1/1/{z}/{x}/{y}.jpg',
projection: gw2Projection
}),
And finally adding it to the view as well:
view: new ol.View({
center: ol.extent.getCenter(extent),
zoom: 1,
projection: gw2Projection,
extent:extent,
resolutions: tilegrid.getResolutions()
})
The entire map is now shown, and the coordinates are correct. The Y-axis is still negative though.
Here's a fiddle with the result, I also included the TileDebug layer for good measure: https://jsfiddle.net/kay99yor/1/
The key to making this work is a custom tile grid definition, i.e. with the correct extent and maxZoom:
var tilegrid = new ol.tilegrid.createXYZ({
extent: gw2Projection.getExtent(),
maxZoom: 7
});
Then configure the XYZ source with that tilegrid:
source: new ol.source.XYZ({
url: 'https://tiles.guildwars2.com/1/1/{z}/{x}/{y}.jpg',
projection: gw2Projection,
tileGrid: tilegrid
})
Also configure the view with the resolutions of that tilegrid:
view: new ol.View({
center: ol.extent.getCenter(gw2Projection.getExtent()),
extent: gw2Projection.getExtent(),
zoom: 1,
resolutions: tilegrid.getResolutions()
}
I have created an updated fiddle that makes your map work: https://jsfiddle.net/kay99yor/.
I am trying to add the new ESRI Vector Basemaps in OpenLayers 3. I have gotten so far as to display the layer un-styled by modifying the Mapbox Example published by OpenLayers.
Clearly I had to remove the style: createMapboxStreetsV6Style() option to get the esri layer to display. So basically the map does not know the style information to display the layer correctly.
I think it should be possible to do it because ESRI's Leaflet port and example is doing this already. I think information on esri's style IDs is available in here Leaflet code.
OpenLayers should already be able to use all this information as it is able to display Mapbox Layer. What I need help with is, how to make it use ESRI's style information.
Here's what I have so far (codepen here):
var map = new ol.Map({
layers: [
new ol.layer.VectorTile({
source: new ol.source.VectorTile({
format: new ol.format.MVT(),
tileGrid: ol.tilegrid.createXYZ({maxZoom: 22}),
tilePixelRatio: 16,
url: 'https://basemaps.arcgis.com/v1/arcgis/rest/services/World_Basemap/VectorTileServer/tile/{z}/{y}/{x}.pbf'
})
})
],
target: 'map',
view: new ol.View({
center: [0, 0],
zoom: 2
})
});
.map {
background: #f8f4f0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.1.0/ol.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/4.1.0/ol.css" rel="stylesheet"/>
<div id="map" class="map"></div>
There is a separate library, https://npmjs.com/package/ol-mapbox-style, which makes it easy to consume vector tile maps including their styles in OpenLayers. It reads the style doc and builds the whole map from it. For one of the ESRI maps you linked above, the code to get that map in OpenLayers would be
var map = new ol.Map({
target: 'map'
});
olms.apply(map, 'https://www.arcgis.com/sharing/rest/content/items/4f4843d99c34436f82920932317893ae/resources/styles/root.json?f=json');
You can replace 4f4843d99c34436f82920932317893ae with one of the other map ids listed in the Leaflet example to get the other maps.
You can also play with that code - I have created a CodePen: https://codepen.io/ahocevar/pen/xLaBVV.
#ahocevar's suggest is perfect,
esri's root.json, sprite and glyphs are not full URL, the are only last part as see below
In your example, those not full URL works, but, I have test it in mapbox js api, it failed, error is can't parse URL,
I have to change those url to a full URL, to make it works.
root_json = {
"version" : 8,
"name": "test",
//"sprite" : original_rootJson.sprite, // original is not a full URL, not work "../sprites/sprite"
// "sprite" : "https://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/2020_USA_Median_Age/VectorTileServer/resources/sprites/sprite",
"sprite" : _sprite,
// "glyphs" : original_rootJson.glyphs, // original is not a full URL, not work "../fonts/{fontstack}/{range}.pbf"
// "glyphs" : "https://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/2020_USA_Median_Age/VectorTileServer/resources/fonts/{fontstack}/{range}.pbf",
"glyphs" : _glyphs,
// root json specification : https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/
"sources" : {
"esri" : {
"type" : "vector",
// By supplying TileJSON properties such as "tiles", "minzoom", and "maxzoom" directly in the source:
"tiles": [
// "https://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/2020_USA_Median_Age/VectorTileServer/tile/{z}/{y}/{x}.pbf"
_tile_pbf
],
// "maxzoom": 14
// By providing a "url" to a TileJSON resource
// not work,yet
// "url" : "https://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/Esri_Childrens_Map/VectorTileServer/tile/{z}/{y}/{x}.pbf"
// "url": "http://api.example.com/tilejson.json"
}
},
"layers": original_rootJson.layers
} // root_json
as title suggest, create Feature and add it to already created vector layer. I am fetching GeoJSON from server and trying to somehow add to vector layer but I can't get it to work... So basically I am asking how to get Feature element from my GeoJSON, so I can add it to vector layer.
What I currently have..
This is my GeoJSON fetched from server :
{"type":"MultiPolygon","coordinates":[[[[20.5629940201429,48.9488601183337],[20.5630121528311,48.9489447276126],[20.563289335522,48.9489141101973],[20.563260061873,48.9488286413488],[20.5629940201429,48.9488601183337]]]]}
next I have addVector function in JavaScript where I'm trying to the magic.(variable GeoJS is GeoJSON fetched from server)
function addVector(geoJS){
var feature = new OpenLayers.Feature.Vector( new OpenLayers.Geometry.MultiPolygon(geoJS) );
vector = new OpenLayers.Layer.Vector("Magic");
map.addLayer(vector);
vector.addFeatures([feature]);
}
and yep I know that second line where I creating feature is wrong, but i cant make it right so i guess id doesn't matter what I write there for now...
I tried it with var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(-70.702451, 42.374473); and it worked position on the map was not where I want it to be but I know that i have to do something with projection... It just doesn't matter now.
and btw I have this
vector = new OpenLayers.Layer.Vector("GeoJSON",
{
projection : "EPSG:4326",
onFeatureInsert : postIns,
strategies : [new OpenLayers.Strategy.Fixed()],
protocol : new OpenLayers.Protocol.HTTP({
url: "test.php",
format: new OpenLayers.Format.GeoJSON()
})
});
And this works, position is where I want it, its perfect except it only works when I make request on my domain, and server I try to reach is on another(I know I can set headers and it would work) but I don't want to do it this way.
So basically I am asking how to get Feature from my GeoJSON. I am really new to OpenLayers so I'm sorry if I asking something obvious.
To use a simplified version of the official example:
var inputGeoJson = '...some-GeoJSON-here...';
var geojson_format = new OpenLayers.Format.GeoJSON();
var vector_layer = new OpenLayers.Layer.Vector();
map.addLayer(vector_layer);
vector_layer.addFeatures(geojson_format.read(inputGeoJson));
You can find more details in the GeoJSON class documentation.
I am having the weirdest problem right now using the ArcGIS Javascript API (v2.4). I'm merely trying to create an instance of an ESRI topo map with an extent, and then add a layer.
Here is the code that works. I create an extent, then a map, then a streetmap layer and then finally add that layer.
var startExtent = new esri.geometry.Extent(-71.5, 42, -71, 42.5, new esri.SpatialReference({wkid:4326}) );
map = new esri.Map("map_canvas", { extent: startExtent,fitExtent:false });
var streetmap = new esri.layers.ArcGISTiledMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer");
map.addLayer(streetmap);
However when I try and do the same thing with a separate server, it doesn't work.
var startExtent = new esri.geometry.Extent(-71.5, 42, -71, 42.5, new esri.SpatialReference({wkid:4326}) );
map = new esri.Map("map_canvas", { extent: startExtent,fitExtent:false });
var basemap = new esri.layers.ArcGISTiledMapServiceLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer");
map.addLayer(basemap);
I know that the faulty-layer's server works, because if I create a map with no extent, it shows the full world imagery server, so it appears that creating a map in with one layer and an extent works, while creating a map with a different layer but the same extent does not work.
Any ideas why?
In your second example, the layer is in web Mercator.
Try converting the extent from geographic to web Mercator before using it in the map constructor.
The easiest way to convert the extent is to use esri..geometry.geographicToWebMercator.