maptalks / maptalks.js

A light and plugable JavaScript library for integrated 2D/3D maps.
https://maptalks.org
BSD 3-Clause "New" or "Revised" License
4.31k stars 503 forks source link

WGS84 ECFF #658

Open munirfarzeen opened 6 years ago

munirfarzeen commented 6 years ago

maptalks's version and what browser you use?

master branch, google chrome

Issue description

https://dominoc925-pages.appspot.com/mapplets/cs_ecef.html I want to display map with ECEF xyz values as can be seen in the link. Is it possible with maptalk?

Please provide a reproduction URL (on any jsfiddle like site)

https://dominoc925-pages.appspot.com/mapplets/cs_ecef.html

fuzhenn commented 6 years ago

It's not directly supported.

You can use third party libraries to convert ecef xyz to wgs84 coordinate, such as ecef-projector

Then:

const projector = require('ecef-projector');

const [lat, lon, alt] = projector.unproject(x, y, z); //xyz is the ecef values

const layer = new maptalks.VectorLayer('v', { enableAltitude : true });
//6378137 is the radius of earth
new maptalks.Marker([lon, lat], { altitude : alt - 6378137 }).addTo(layer);
munirfarzeen commented 6 years ago

No, this is not what i wanted to do, maybe i was unclear. I have to make a map in ECEF coordinate system. Not just one point. any idea how to do that? do i have to make a CRF?

fuzhenn commented 6 years ago

I think it's possible, but depends on what you want.

maptalks's coordinate system is also a cartesian XYZ system naturally fit for ECEF's need .

So you may use maptalks to present ECEF system like this:

const map = new maptalks.Map('map', {
     center : [0, 0],
     spatialReference : {
         projection : 'identity', //identity means using cartesian system without any projection
        'resolutions': (function () {
            let res = Math.pow(2, 8);
            const resolutions = [];
            for (let i = 0; i < 18; i++) {
                resolutions[i] = res;
                res *= 0.5;
            }
            return resolutions;
        })(),
        'fullExtent': {
            'top': 6378137,
            'left': -6378137 ,
            'bottom': -6378137 ,
            'right': 6378137
        }
     }
});
const layer = new maptalks.VectorLayer('v', { enableAltitude : true }).addTo(map);
//x, y can be ECEF's [x, y] or [x, z] or [y, z]
//it depends on the main axes you choose for maptalks's ground plane
new maptalks.Marker([x, y], { altitude : z }).addTo(layer);
munirfarzeen commented 6 years ago

hi, thank you so much for the reply. but when i changed the code accordingly, i have no background image. should i add the baselayer from the openstreet ? oe should have own map?

munirfarzeen commented 6 years ago

i mean there is no map, just the empty page. URL from OPenstreet would work>?

fuzhenn commented 6 years ago

Unfortunately, base map from openstreetmap can't be used in ECEF maps as it's a wgs84 REQUIRED map source. 😞

If you want to use ECEF data on openstreetmap maps, you have to convert ECEF data to wgs84 at first.

munirfarzeen commented 6 years ago

how do you suggest i do that? any idea?

munirfarzeen commented 6 years ago

i have one basic question, as far my understanding goes ECEF is cartesian XYZ system. and WGS84 is just a standard to define the shape of the earth? now when you say i have to convert ECEF data to wgs84, what exactly that mean? will the data still in XYZ. because the literature i studied says that openstreet maps uses WGS48 system but they have latitude and longitude. kindly point me right direction

fuzhenn commented 6 years ago

Yeah, your understanding is right, WGS84 is a standard to define any position on the earth, it's an ellipsoid coordinate system that is totally different from cartesian XYZ.

Thus, to use osm basemap, you have to convert your ECEF data to wgs84 before adding to maps.

You can convert on your own with ecef-projector or sth else before creating maps.

Or, to make it easier for this conversion, you can create a customized projection object to convert ECEF data on the fly to wgs84 and then to mercator 3857(required by osm). This can let you input ecef data directly to maps. You can refer to this example of custom projection.

Replace proj4js in the example with ecef-projector like this:

const projector = require('ecef-projector');

const proj3857 = maptalks.projection.EPSG3857;
const projection = {
   code : 'myecef',
   project(ecef) : {
      //as maptalk's coordinate only has [x,y], fix altitude to 6378137(the surface of earth)
      const [lat, lon, alt] = projector.unproject(ecef.x, ecef.y, 6378137);
      //convert wgs84 to 3857
      return proj3857.project(new maptalks.Coordinate(lon, lat));
   },
   unproject(coord): {
      //convert 3857 to wgs84
      const lonlat = proj3857.unproject(coord);
      const ecef = projector.project(lonlat.y, lonlat.x, 0);
      return new maptalks.Coordinate(ecef[0], ecef[1]));
  }
};

const map = new maptalks.Map('map', {
        center:     [-0.113049, 51.498568],
        zoom:  13,
        // spatial reference definition
        spatialReference : {
          projection : projection,        // geo projection
          resolutions : [           // map's zoom levels and resolutions
            156543.03392804097,
            78271.51696402048,
            9135.75848201024,
            19567.87924100512,
            9783.93962050256,
            4891.96981025128,
            2445.98490512564,
            1222.99245256282,
            611.49622628141,
            305.748113140705,
            152.8740565703525,
            76.43702828517625,
            38.21851414258813,
            19.109257071294063,
            9.554628535647032,
            4.777314267823516,
            2.388657133911758,
            1.194328566955879,
            0.5971642834779395,
            0.29858214173896974
          ],
          fullExtent : {         // map's full extent
            'top': 6378137 * Math.PI,
            'left': -6378137 * Math.PI,
            'bottom': -6378137 * Math.PI,
            'right': 6378137 * Math.PI
          }
        },
        baseLayer : new maptalks.TileLayer('base',{
          urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
          subdomains: ['a','b','c','d'],
          attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
        })
      });

The above codes are not tested.

munirfarzeen commented 6 years ago

Actually i dont have ECEF data, i making application specific map, that is able to tell me my location in XYZ coordinates. I dont require latitude and longitude on my map. I just wanna know my location in XYZ does this makes sense? or is it possible?

fuzhenn commented 6 years ago

So let me sum it up: you want to create a map with osm baselayer, and get locations on the map in XYZ coodinates? Am I right?

If I'm right, just create a map with customized projection mentioned above, you can get any XYZ location you need on the map.

munirfarzeen commented 6 years ago

yes, i want to use the titles from any public server and able to get the map in XYZ, and dimension would be in meters. Like if i travel one meter on map which should be equal to same one meter on earth so, if i follow those projections, i will be able to do that?

fuzhenn commented 6 years ago

Yes, I think so.

You can have a try and return to me if you need any help any time.

munirfarzeen commented 6 years ago

okay, thank you so much for your time. I will try it.

munirfarzeen commented 6 years ago

hi again, i am new to javascript too. the line const projector = require('ecef-projector'); gives error uncaught reference error. it says require is a server side function not the client side. i tried including .js file in script, than i get the same error later in file. any way to solve it

fuzhenn commented 6 years ago

Sorry mate, I only provide support for this library. 😞

eLBirador commented 1 year ago

Hi @fuzhenn ,

Thank you for this library. My question is related to this thread, maybe...

I am trying to load the basic example of the map and point the center to -33.856680307503595, 151.21530689724574 (approximately Sydney opera house) based on the coordinates I got from google maps. somehow when the map is loaded its pointed somewhere else

const map = new maptalks.Map(mapName, {
        center: [-33.856680307503595, 151.21530689724574],
        zoom: 2,
        baseLayer: new maptalks.GroupTileLayer('base-map', [
          new maptalks.TileLayer('white-map', {
            urlTemplate : 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
            subdomains  : ['a','b','c','d'],
            attribution  : '&copy; <a href="http://www.osm.org/copyright">OSM</a> contributors, '+
            '&copy; <a href="https://carto.com/attributions">CARTO</a>'
          })
        ])
      })

I tried playing around spatialReference option to change the projection to WGS84 but Im getting invalid projection error.

Do you have any idea what I might be missing?

deyihu commented 1 year ago

@eLBirador The coordinates of the center point are reversed. In the maptalks system, the longitude and latitude in the coordinates are longitude before latitude after latitude.

so The correct code is

const map = new maptalks.Map(mapName, {
        center: [-33.856680307503595, 151.21530689724574].reverse(),
        zoom: 2,
        baseLayer: new maptalks.GroupTileLayer('base-map', [
          new maptalks.TileLayer('white-map', {
            urlTemplate : 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
            subdomains  : ['a','b','c','d'],
            attribution  : '&copy; <a href="http://www.osm.org/copyright">OSM</a> contributors, '+
            '&copy; <a href="https://carto.com/attributions">CARTO</a>'
          })
        ])
      })