7 votes

Comment ajuster la taille du zoom pour un point dans D3 ?

Il pourrait s'agir d'un cas classique de "vous vous y prenez mal", mais toutes mes recherches jusqu'à présent n'ont pas permis de trouver de l'aide.

Voici mon scénario :

J'utilise une projection cartographique albersUSA en conjonction avec les fichiers GeoJson du pays et du comté pour tout dessiner.

J'ai également un fichier "villes" que j'ai créé moi-même et qui contient les principales villes de chaque État. Les coordonnées sont exactes et tout semble correct.

enter image description here

Lorsqu'un utilisateur clique sur un état donné, je masque toutes les formes de l'état, puis je calcule la transformation nécessaire pour que les formes du comté de cet état tiennent dans ma fenêtre. J'applique ensuite cette transformation à toutes les formes de comtés nécessaires afin d'obtenir la vue "zoomée". Mon code est le suivant :

function CalculateTransform(objectPath)
{
   var results = '';

   // Define bounds/points of viewport
   var mapDimensions = getMapViewportDimensions();
   var baseWidth = mapDimensions[0];
   var baseHeight = mapDimensions[1];

   var centerX = baseWidth / 2;
   var centerY = baseHeight / 2;

   // Get bounding box of object path and calculate centroid and zoom factor
   // based on viewport.
   var bbox = objectPath.getBBox();
   var centroid = [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2];
   var zoomScaleFactor = baseHeight / bbox.height;
   var zoomX = -centroid[0];
   var zoomY = -centroid[1];

   // If the width of the state is greater than the height, scale by
   // that property instead so that state will still fit in viewport.
   if (bbox.width > bbox.height) {
      zoomScaleFactor = baseHeight / bbox.width;
   }

   // Calculate how far to move the object path from it's current position to
   // the center of the viewport.
   var augmentX = -(centroid[0] - centerX);
   var augmentY = -(centroid[1] - centerY);

   // Our transform logic consists of:
   // 1. Move the state to the center of the screen.
   // 2. Move the state based on our anticipated scale.
   // 3. Scale the state.
   // 4. Move the state back to accomodate for the scaling.   
   var transform = "translate(" + (augmentX) + "," + (augmentY) + ")" +
                 "translate(" + (-zoomX) + "," + (-zoomY) + ")" +
                 "scale(" + zoomScaleFactor + ")" +
                 "translate(" + (zoomX) + "," + (zoomY) + ")";

   return results;
}

...et la fonction de liaison

// Load county data for the state specified.
d3.json(jsonUrl, function (json) {
    if (json === undefined || json == null || json.features.length == 0) 
    {
       logging.error("Failed to retrieve county structure data.");
       showMapErrorMessage("Unable to retrieve county structure data.");
       return false;
    }
    else 
    {
       counties.selectAll("path")
                .data(json.features)
                .enter()
                   .append("path")
                      .attr("id", function (d, i) {
                         return "county_" + d.properties.GEO_ID
                      })
                      .attr("data-id", function (d, i) { return d.properties.GEO_ID })
                      .attr("data-name", function (d, i) { return countyLookup[d.properties.GEO_ID] })
                      .attr("data-stateid", function (d, i) { return d.properties.STATE })
                      .attr("d", path);

        // Show all counties for state specified and apply zoom transform.
        d3.selectAll(countySelector).attr("visibility", "visible");
        d3.selectAll(countySelector).attr("transform", stateTransform);

        // Show all cities for the state specified and apply zoom transform
        d3.selectAll(citySelector).attr("visibility", "visible");
        d3.selectAll(citySelector).attr("transform", stateTransform);
    }
});

enter image description here

Cela fonctionne bien ici, sauf pour les états vraiment petits, le facteur de zoom est beaucoup plus grand, et les cercles se déforment.

enter image description here

Existe-t-il un moyen de forcer la taille des points à être une taille fixe (disons un rayon de 15px) même après la transformation ?

6voto

Tong Gao Points 61

Pour les choses que vous ne voulez pas mettre à l'échelle, il suffit de les diviser par 'scale'. Dans mon cas,

var zoom = d3.behavior.zoom()
    .on("zoom",function() {
        g.attr("transform","translate("+d3.event.translate.join(",")+")scale("+d3.event.scale+")");

        g.selectAll(".mapmarker")  
        .attr("r",6/d3.event.scale)
        .attr("stroke-width",1/d3.event.scale);

});

4voto

Superboggly Points 2943

Cela se produit parce que vous définissez une transformation d'échelle au lieu de mettre à l'échelle les positions. Vous pouvez voir la différence ici En gros, c'est la différence entre :

// Thick lines because they are scaled too
var bottom = svg.append('g').attr('transform', 'scale('+scale+','+scale+')');
bottom.selectAll('circle')
    .data(data)
    .enter().append('circle')
    .attr('cx', function(d) { return d.x; })
    .attr('cy', function(d) { return d.y; });

et

// line thicknesses are nice and thin
var top = svg.append('g');
top.selectAll('circle')
    .data(data)
    .enter().append('circle')
    .attr('cx', function(d) { return d.x * scale; })
    .attr('cy', function(d) { return d.y * scale; });

Avec la cartographie, la meilleure solution est probablement de calculer votre décalage et votre échelle comme vous le faites, puis de les ajouter dans votre fonction de projection - vous voulez modifier directement les valeurs x et y post-projection. Si vous mettez à jour votre fonction de projection correctement, vous ne devriez pas avoir à faire quoi que ce soit d'autre pour appliquer le zoom approprié à votre carte.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X