Skip to content
Advertisement

How to create a geometry for each feature in a stringified geometry collection from a BigQuery JavaScript UDF?

I’m new to UDF functions and I have created a BigQuery UDF that takes a polygon geometry and creates points with it. I’m trying to draw dot density maps (converting polygon + population number to points). I’ve adapted the code from this blog post. Because bigQuery doesn’t have a way to log variables, I’ve been testing things out in this codepen.

I’m at a point where the function seems to work right. The output is a geometry collection of points. It says in the bigquery docs that st_geogfromgeojson can accept a geometry collection.

My UDF returns a stringified geometry collection.

But I cannot figure out why st_geogfromgeojson doesn’t work. I can’t tell if I’m simply not unnesting something or what.

CREATE TEMP FUNCTION myFunc(feature string, ethnicity_column FLOAT64, year INT64)
  RETURNS string
  LANGUAGE js
  OPTIONS (
    library=["https://storage.googleapis.com.../d3.js","https://storage.googleapis.com/.../turf.min.js","https://storage.googleapis.com/.../wellknown.js"]
  )
  AS
    """
          
        if (feature === undefined || feature === null) return;
       
        
        var feature_parsed = wellknown.parse(feature)
       
        const bounds = turf.bbox(feature_parsed);
        
        const populationData = Math.round(ethnicity_column / 10);
        if (!populationData) return;
       
         const x_min = bounds[0];
         const y_min = bounds[1];
         const x_max = bounds[2];
         const y_max = bounds[3];
  
        let hits = 0;
        let count = 0;
        const limit = populationData * 10; // limit test to 10x the population.
        let points = [];
        while (hits < populationData - 1 && count < limit) {
          const lat = y_min + Math.random() * (y_max - y_min);
          const lng = x_min + Math.random() * (x_max - x_min);
          const randomPoint = turf.point([lng, lat]);
          if (turf.booleanPointInPolygon(randomPoint, feature_parsed)) {
            points.push(randomPoint);
            hits++;
          }
          count++;
        }
        return JSON.stringify((turf.geometryCollection(points)));
        
       // return JSON.stringify(points)

    """;


SELECT ST_GEOGFROMGEOJSON(JSON_EXTRACT((myFunc(st_astext(geom), white_pop, 2018)),'$')) FROM `myteam.kyle_data.blockgroups_with_acs` 

But I keep hitting random errors like I’m not using the function right enter image description here

I’m open to all suggestions. I return a string for simplicity but perhaps I need to use a STRUCT. Perhaps I should cut out turf from creating the points? I must be missing something here.

Advertisement

Answer

Two things that fixed it:

  • Returning array<String> instead of string
  • CROSS JOIN UNNEST in the select
CREATE TEMP FUNCTION myFunc(feature string, ethnicity_column FLOAT64)
  RETURNS array<String>
  LANGUAGE js
  OPTIONS (
    library=["https://storage.googleapis.com/../d3.js","https://storage.googleapis.com/.../turf.min.js","https://storage.googleapis.com/.../wellknown.js"]
  )
  AS
    """
        geopath = d3.geoPath()
        if (feature === undefined || feature === null) return;
        var feature_parsed = wellknown.parse(feature)
        const bounds = turf.bbox(feature_parsed);
        const populationData = Math.round(ethnicity_column / 10);
        if (!populationData) return;
         const x_min = bounds[0];
         const y_min = bounds[1];
         const x_max = bounds[2];
         const y_max = bounds[3];
        let hits = 0;
        let count = 0;
        const limit = populationData; // limit test to 10x the population.
        let points = [];
        while (hits < populationData - 1 && count < limit) {
          const lat = y_min + Math.random() * (y_max - y_min);
          const lng = x_min + Math.random() * (x_max - x_min);
          const randomPoint = turf.point([lng, lat]);
          if (turf.booleanPointInPolygon(randomPoint, feature_parsed)) {
            points.push('POINT ('+lng+' '+lat+')');
            hits++;
          }
          count++;
        }
        return points;
    """;
SELECT st_geogfromtext(points) as the_geom,  'white' as ethnicity  from (SELECT (myFunc(st_astext(geom), white_pop)) as points FROM `tableonmybq`) CROSS JOIN UNNEST(points) as points
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement