Coder Social home page Coder Social logo

Comments (28)

rbrundritt avatar rbrundritt commented on August 10, 2024 1

Tried the code provided by @perfahlen and it works fine after adding a subscription key to the map options. I've added a modified version of Perfahlen's code to the sample site here: https://azuremapscodesamples.azurewebsites.net/index.html?sample=Get%20closest%20HTML%20marker%20to%20position

@sujayvsarma Can you verify that your are pointing to version 2 of the Azure Maps Web SDK (check the javascript URL and make sure if has "mapcontrol/2/atlas.min.js" in it. If it doesn't have then you are likely pointing to a really old version that doesn't have the html marker manager.

Just a general piece of feedback, if you are trying to load a lot of points on the map (more than a few hundred) consider using a data source and the symbol or bubble layer instead of HTML markers. HTML markers create DOM elements on the page and the more DOM elements on a page, the slower the page becomes (standard web dev problem). The data source and symbol/bubble layers support hundreds of thousands of points with good performance. This is noted in this documentation on HTML markers. I have put together a sample for this scenario here: https://azuremapscodesamples.azurewebsites.net/index.html?sample=Get%20closest%20point%20to%20position

from azuremapscodesamples.

 avatar commented on August 10, 2024

Tagging @walsehgal, @stack111, @rbrundritt

from azuremapscodesamples.

perfahlen avatar perfahlen commented on August 10, 2024

@sujayvsarma , if your dataset is small, less than a couple of hundred points you can use azure maps math library, getDistanceTo: https://docs.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.math?view=azure-maps-typescript-latest#getdistanceto-position---point--position---point--string---distanceunits- and simple loop over them to find the closest point. However, this is not to recommend if it's a task you using frequently, it is a task rarely used. Otherwise you need to find another solution.

or you can use https://docs.microsoft.com/en-us/javascript/api/azure-maps-control/atlas.math?view=azure-maps-typescript-latest#getclosestpointongeometry-position---point---feature-point--any---atlas--string---distanceunits--number- but this method requires you to create a multipoint of all the other points first.

Example using

var p1 = new atlas.data.Point([20, 60]);
var p2 = new atlas.data.Point([10, 50]);
console.log(atlas.math.getDistanceTo(p1, p2));

from azuremapscodesamples.

 avatar commented on August 10, 2024

@perfahlen - Yeah so to be able to get to that calculation, I need to get the data out of the marker. How do I get the marker and its data from atlas after I have added it?

from azuremapscodesamples.

perfahlen avatar perfahlen commented on August 10, 2024

@sujayvsarma for example, shape.data

from azuremapscodesamples.

 avatar commented on August 10, 2024

What.. no, you cant get it that way. Here is what I have done.

I get the custom POI information (in my case, these are airports) from a different data source. These are added to the map using this code:

var marker = new atlas.HtmlMarker({
   htmlContent: amSvg,
   color: '#343a40',
   text: data[0].iata,
   position: [data[0].geoLocation.lon, data[0].geoLocation.lat]
});

map.markers.add(marker);

(amSvg is an SVG pin shape).

Both the HtmlMarker and the map itself have click events attached. They both fire correctly.

When the click is on the HtmlMarker, I can get the data I want (in this case, the text value) using e.target.options.text.

BUT, when the click is on the map, I want to know if the user has clicked on the Azure Maps control's own POI for that same airport. I can get to this using:

if ((e.shapes[0].type === 'Feature') && (e.shapes[0].layer.id === 'Airport label'))

and then examining the properties therein.

BUT, what I want to do here is, I want to get the nearest HtmlMarker to the Maps' own POI position and move the user to that instead.

from azuremapscodesamples.

 avatar commented on August 10, 2024

To clarify -- I want to start with an Azure Maps' POI and move to a (the nearest) custom HtmlMarker.

from azuremapscodesamples.

perfahlen avatar perfahlen commented on August 10, 2024

@sujayvsarma you should be able to get position from

marker.getOptions().position

from azuremapscodesamples.

 avatar commented on August 10, 2024

If you don't know the answer, please don't spam the thread with irrelevant answers. It serves no purpose. At least you should read the question and respond to that.

How do I get the marker?????? That is my biggest problem.

Find nearest HtmlMarker to click position

That is what I want.. I don't know how to get it.

from azuremapscodesamples.

perfahlen avatar perfahlen commented on August 10, 2024

sorry @sujayvsarma , no need not to keep a good tone. I should have read your problem more thorough,

I made an example that locates the nearest HTML marker clicked and color that marker blue for 1.5 seconds. Hope it helps

<html>
  <head>
    <title>
    </title>
    <link
      rel="stylesheet"
      href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css"
      type="text/css"
    />
    <script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"></script>

    <script type="text/javascript">
      var map;
      function GetMap() {
        map = new atlas.Map("myMap", {
          view: "Auto",
          authOptions: {
            authType: "subscriptionKey",
            subscriptionKey: "<KEY HERE>"
          }
        });
        map.events.add("ready", function() {
          map.markers.add(
            new atlas.HtmlMarker({
              htmlContent:
                '<div style="width: 10px; height: 10px; background-color: red"></div>',
              position: [0, 0]
            })
          );

          map.markers.add(
            new atlas.HtmlMarker({
              htmlContent:
                '<div style="width: 10px; height: 10px; background-color: red"></div>',
              position: [50, 50]
            })
          );
        });

        map.events.add("click", function(evt) {
          let position = evt.position;
          let markers = map.markers.getMarkers();
          let closestMarker = {};
          markers.forEach(marker => {
            if (!closestMarker.marker) {
              closestMarker.marker = marker;
              closestMarker.distance = atlas.math.getDistanceTo(
                new atlas.data.Point(marker.getOptions().position),
                new atlas.data.Point(evt.position)
              );
            }
            let distance = atlas.math.getDistanceTo(
              new atlas.data.Point(marker.getOptions().position),
              new atlas.data.Point(evt.position)
            );
            if (distance < closestMarker.distance) {
              closestMarker.marker = marker;
              closestMarker.distance = distance;
            }
          });
          let options = closestMarker.marker.getOptions();
          closestMarker.marker.setOptions({
            htmlContent:
              '<div style="width: 10px; height: 10px; background-color: blue"></div>'
          });

          setTimeout(() => {
            closestMarker.marker.setOptions({
              htmlContent:
                '<div style="width: 10px; height: 10px; background-color: red"></div>'
            });
          }, 1500);
        });
      }
    </script>
  </head>
  <body onload="GetMap()">
    <div
      id="myMap"
      style="position:relative;width:100%;min-width:290px;height:600px;background-color:gray"
    ></div>
  </body>
</html>

from azuremapscodesamples.

 avatar commented on August 10, 2024

I already tried this before posting here. I get runtime JavaScript errors that these are not implemented:

let markers = map.markers.getMarkers()

Error message that markers doesn't implement getMarkers.

markers.forEach(marker...

Error message that markers doesn't implement forEach.

I tried the statements directly in the F12 console as well, same result.

from azuremapscodesamples.

 avatar commented on August 10, 2024

@rbrundritt

I am pointing to v2 of the SDK. This is my script line:

<script type="text/javascript" src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"></script>

How do I adapt the HtmlMarker way to using a SymbolLayer instead? I could not find any documentation. How would I go about getting custom POI information into a data source that the SymbolLayer uses?

This is the thing.. There is hardly any documentation around these things!

What browser are you folks using? I am using Microsoft Edge (on Win10).

from azuremapscodesamples.

 avatar commented on August 10, 2024

I got it to work. Finally! Had a typo in the code. Instead of map.markers.getMarkers(), I was doing map.Markers.getMarkers() (with a capital M on the property).


Now, can you help me for this?

How do I adapt the HtmlMarker way to using a SymbolLayer instead? I could not find any documentation. How would I go about getting custom POI information into a data source that the SymbolLayer uses?

from azuremapscodesamples.

rbrundritt avatar rbrundritt commented on August 10, 2024

I provided a like in my response to a sample that does exactly what you asked using a symbol layer. There is a source code button on all samples.

There is a ton of documentation on Azure maps. I recommend going through the how to guides for the web SDK first https://docs.microsoft.com/en-us/azure/azure-maps/how-to-use-map-control

Look at the tavle of contents on the side to see all the different topics. I recommend you start with the create a data source doc then going to the add a symbol layer doc. There is also over 150 code samples here https://azuremapscodesamples.azurewebsites.net

from azuremapscodesamples.

 avatar commented on August 10, 2024

Do you know of a practical threshold beyond which a collection of HtmlMarkers would start affecting performance? In my scenario, I am expecting to have at most 10 such markers.

from azuremapscodesamples.

rbrundritt avatar rbrundritt commented on August 10, 2024

Depends on device but usually around 300 you might notice a bit off a hit. Over 1000 and you will certainly notice. If only using 10 html markers then you are fine.

from azuremapscodesamples.

 avatar commented on August 10, 2024

Oh. I should be okay then. :)

Question for @rbrundritt :
When using the Closest Marker calculation code from your samples above (I actually took the one from your sample, it sometimes finds locations that are off. For example, in your own sample, you can see how it sometimes finds markers in a different geographical country.

Is there a way to restrict the math by geographic boundaries? As discussed above, my UX is about airports. I don't want to show the user that the closest airport to a point is in a different country (while mathematically true, it would be practically undesirable).

from azuremapscodesamples.

rbrundritt avatar rbrundritt commented on August 10, 2024

That can be achieved as well, but if you don't want users to go there, then perhaps the better approach is not to show them on the map in the first place.

To verify, is the airport data from the Azure Maps POI service?
Do you want the user to be able to click anywhere in the world and then based on where they clicked, limit the nearest airport for the country they clicked in?
If they click on water, what do you want to do?

from azuremapscodesamples.

 avatar commented on August 10, 2024

is the airport data from the Azure Maps POI service?

No. We've already discussed Azure POI data and it's usability for my purpose in another item. Don't want to go there again.

Do you want the user to be able to click anywhere in the world and then based on where they clicked, limit the nearest airport for the country they clicked in?

Yes.

If they click on water, what do you want to do?

Physically nearest would be suitable, I guess.

from azuremapscodesamples.

rbrundritt avatar rbrundritt commented on August 10, 2024

Does your airport data have country iso codes as properties or some other information we can use to link them to a country? If not, then there are a couple of different approaches. One is to enrich the data with that info. If you have this already, then great otherwise passing all airports through a reverse geocoder would work, but wouldn't be very good for costs. The other is to retrieve the boundary for country then doing a point in polygon search. Not ideal in terms of costs.

from azuremapscodesamples.

 avatar commented on August 10, 2024

Yes, the airport data has country codes. But, how would I add this metadata to the HtmlMarker?

from azuremapscodesamples.

perfahlen avatar perfahlen commented on August 10, 2024

@sujayvsarma ,

Solution 1
You can use HtmlMarkerLayer as @rbrundritt's example here https://github.com/Azure-Samples/AzureMapsCodeSamples/tree/master/AzureMapsCodeSamples/HTML%20Markers/HtmlMarkerLayer

Solution 2
I'm not sure if this is ok with license agreement. You need to handle the attributes yourself. One way is to simple extend HTML Marker with your own getter and setter.

      atlas.HtmlMarker.prototype.setCountry = function(country){
          this._country = country;
      };

      atlas.HtmlMarker.prototype.getCountry = function(){
          return this._country;
      };

then you can simple

marker.setCountry('a');
and to access it you can
marker.getCountry();

Solution 3
or you can simple hide it your htmlContent like

<div style="width: 10px; height: 10px; background-color: red;" data-country="a"></div>

and filter it when you iterate markers.

from azuremapscodesamples.

rbrundritt avatar rbrundritt commented on August 10, 2024

To attach custom data to HTML Markers you can add your data to a custom property of the class. We recommend using "property" for consistency with the rest of the map API. This is similar to what PreFahlen's solution 2 above, but just a property rather than getters/setters. For example, lets assume your data is in GeoJSON format, you can add the properties to the marker like so:

var myDataPoint = {
	type: 'Feature',
	geometry: {
		type: 'Point',
		coordinates: [0, 0]
	},
	properties: {
		countryIso: 'US'
	}
};

var marker = new atlas.HTMLMarker({
	position: myDataPoint.geometry.coordinate
});

//Attach the properties to the marker.
marker.properties = myDataPoint.properties;

Later, when you retrieve the marker from say a calculation, it will have your customer data available on the "properties" property of the marker. You can do this kind of thing with pretty much any element in HTML. This is a general web development thing and not specific to Azure Maps.

Now, lets assume you have an array of markers similar to the one above added to the map and you want to find the closest one that is in the country the user clicked in. The first step is to find out what country the user clicked in. There are two ways to do this;

  1. Use a reverse geocoding service. This will generate a transaction, but would be a supported method. I have attached a code sample using this approach to this post. I used some of airport data from here http://ourairports.com/data/airports.csv to create a geojson file to power the sample.
  2. This is an unsupported method currently, but something we actually do under the hood of the map control to power the screen reader for those with limited vison. The map uses vector tiles to render the base map. These vector tiles has the raw map data in it, like country boundaries and road lines. All road data have a country ISO 3 value on them. You can access the base map data using some undocumented functions. This method would only work if there is road data rendered on the map and wouldn't be perfect, but it would be practically free (only cost being that map tiles have to be rendered, which you likely want anyways). Since this approach is a decent amount of work, error prone and not currently supported I won't put together a sample for this at the moment, but something that might be worth experimenting with. I have a sample showing how to access this data here: https://azuremapscodesamples.azurewebsites.net/experimental/Inspect%20features%20under%20the%20mouse.html The source code is here: https://github.com/Azure-Samples/AzureMapsCodeSamples For fun I tried a large data set of 9,000+ airports, loading and moving the map were slow, but the search logic in this sample worked fast. If you are interested in giving this a try replace airport.js with airport_big.js in the script tag at the top of the html file.

Airport Sample.zip

from azuremapscodesamples.

 avatar commented on August 10, 2024

@rbrundritt

I had a theory I could add custom data to properties and stuff. But I wasn't sure if the control's existing code would play nice with that. For example, what if I added something that internally meant something else? Or the values got overwritten by other code? Seemed to be iffy to go that route, with a lot of trial and error.

#2 -- I was looking at your hosted sample. It looks like the data is only available if you click on a road. If you click somewhere else (like a "built-up area" or the name of the city), then the data is not available. Since I cannot force the user to only click on roads, this solution doesn't seem feasible in my scenario :( Though the sample and how could do that was very interesting.

from azuremapscodesamples.

rbrundritt avatar rbrundritt commented on August 10, 2024

Yes, if you created a customer property with the same name of an existing property it would break things. That's why we recommend using the name "properties" or "metadata". Internally we know not to use those and have documented that for layers (need to update docs for HTML markers).

For #2, that sample just grabs when you clicked on, but the code can easily be modified to grab the closest road that has been loaded on the map. This approach is more complex, but as I said wouldn't generate any cost. What you could do, if you want the cheapest possible option, is to combine both methods. Try and get the nearest road, but set a threshold. If the road is more than x miles away from where the user clicked or no road found, fallback to reverse geocoder.

from azuremapscodesamples.

 avatar commented on August 10, 2024

Thank you @rbrundritt. You have been more than helpful :)

I shall now close this item because I believe I have no further questions at this time.

from azuremapscodesamples.

 avatar commented on August 10, 2024

@rbrundritt - What's your MS alias? Wanted to e-mail you (off this public channel)

from azuremapscodesamples.

rbrundritt avatar rbrundritt commented on August 10, 2024

richbrun

from azuremapscodesamples.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.