codeant-ai-for-open-source[bot] commented on code in PR #39760:
URL: https://github.com/apache/superset/pull/39760#discussion_r3613252952


##########
superset-frontend/plugins/plugin-chart-cartodiagram/src/util/mapUtil.tsx:
##########
@@ -50,3 +57,157 @@ export const fitMapToCharts = (olMap: Map, chartConfigs: 
ChartConfig) => {
     size: [250, 250],
   });
 };
+
+const fitToData = (
+  olMap: Map,
+  features: Feature[],
+  padding?: FitOptions['padding'] | undefined,
+) => {
+  const view = olMap.getView();
+  const extent = getExtentFromFeatures(features) || defaultExtent;
+
+  if (padding) {
+    view.fit(extent, { padding });
+  } else {
+    view.fit(extent);
+  }
+};
+
+const extractSridFromWkt = (wkt: string) => {
+  const extract: { geom: string; srid: string | null } = {
+    geom: wkt,
+    srid: null,
+  };
+  if (wkt.startsWith('SRID=')) {
+    // WKT with SRID, strip it
+    const srid = wkt.match(/SRID=(\d+);/);
+    if (srid) {
+      extract.srid = `EPSG:${srid[1]}`;
+      extract.geom = wkt.replace(/SRID=\d+;/, '');
+    }
+  }
+  return extract;
+};
+
+export const wkbToGeoJSON = (wkb: string) => {
+  const format = new WKB();
+  const feature = format.readFeature(wkb, {
+    featureProjection: 'EPSG:3857',
+  });
+  return new GeoJSON().writeFeatureObject(feature, {
+    featureProjection: 'EPSG:3857',
+  });
+};
+
+export const wktToGeoJSON = (wkt: string) => {
+  const format = new WKT();
+  const wktOpts: ReadOptions = {
+    featureProjection: 'EPSG:3857',
+    dataProjection: 'EPSG:4326', // default to WGS84
+  };
+  const extract = extractSridFromWkt(wkt);
+  const cleanedWkt = extract.geom;
+  if (extract.srid) {
+    wktOpts.dataProjection = extract.srid;
+  }
+  const feature = format.readFeature(cleanedWkt, wktOpts);
+  return new GeoJSON().writeFeatureObject(feature, {
+    featureProjection: 'EPSG:3857',
+  });
+};
+
+/**
+ * Create OL Features from data records.
+ *
+ * @param dataRecords The data records to transform.
+ * @param geomColumn The name of the column holding the geodata.
+ * @returns List of OL Features.
+ */
+export const dataRecordsToOlFeatures = (
+  dataRecords: DataRecord[],
+  geomColumn: string,
+  geomFormat: GeometryFormat.WKB | GeometryFormat.WKT,
+) => {
+  let format: WKB | WKT = new WKB();
+
+  if (geomFormat === GeometryFormat.WKT) {
+    format = new WKT();
+  }
+  const features = dataRecords
+    .map(item => {
+      const geom = item[geomColumn];
+      if (typeof geom !== 'string') {
+        return undefined;
+      }
+
+      let cleanedGeom = geom;
+      const opts: ReadOptions = {
+        featureProjection: 'EPSG:3857',
+      };
+      if (geomFormat === GeometryFormat.WKT) {
+        const extract = extractSridFromWkt(geom);
+        cleanedGeom = extract.geom;
+        if (extract.srid) {
+          opts.dataProjection = extract.srid;
+        }
+      }
+      const feature = format.readFeature(cleanedGeom, opts);
+      feature.setProperties({ ...item });

Review Comment:
   **Suggestion:** A single malformed WKT/WKB value will throw from 
`readFeature` and abort processing of the entire dataset. Wrap parsing per 
record and skip invalid geometries so one bad row does not crash map 
fitting/rendering. [possible bug]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ❌ Cartodiagram map crashes on malformed WKT/WKB record.
   - ⚠️ Map extent fitting fails for affected datasets.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. In 
`superset-frontend/plugins/plugin-chart-cartodiagram/src/util/mapUtil.tsx:189`, 
call
   the exported function `fitMapToDataRecords(olMap, dataRecords, geomColumn, 
geomFormat,
   padding)` with `geomFormat` set to `GeometryFormat.WKT` or 
`GeometryFormat.WKB`.
   
   2. Ensure `dataRecords` includes at least one record where 
`dataRecord[geomColumn]` is a
   string that is not valid WKT/WKB (for example, a truncated or otherwise 
malformed geometry
   string).
   
   3. Inside `fitMapToDataRecords`, at `mapUtil.tsx:196-201`, the function calls
   `dataRecordsToOlFeatures(dataRecords, geomColumn, geomFormat)` to convert 
each record into
   OpenLayers `Feature` instances.
   
   4. In `dataRecordsToOlFeatures` (`mapUtil.tsx:126-161`), when processing the 
malformed
   record, `format.readFeature(cleanedGeom, opts)` at line 154 throws from the 
OpenLayers
   WKT/WKB parser; because this exception is not caught, it aborts 
`dataRecordsToOlFeatures`,
   `fitToData` (`mapUtil.tsx:61-74`), and thus `fitMapToDataRecords`, causing 
any caller
   using this helper to fail rendering/fitting the cartodiagram map.
   ```
   </details>
   
   [![Fix in 
Cursor](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-cursor-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=1a47b2a8e4f840159f460a321fd9d019&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 [![Fix in VSCode 
Claude](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-vscode-claude-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=1a47b2a8e4f840159f460a321fd9d019&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
   
   *(Use Cmd/Ctrl + Click for best experience)*
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/plugin-chart-cartodiagram/src/util/mapUtil.tsx
   **Line:** 154:155
   **Comment:**
        *Possible Bug: A single malformed WKT/WKB value will throw from 
`readFeature` and abort processing of the entire dataset. Wrap parsing per 
record and skip invalid geometries so one bad row does not crash map 
fitting/rendering.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   Once fix is implemented, also check other comments on the same PR, and ask 
user if the user wants to fix the rest of the comments as well. if said yes, 
then fetch all the comments validate the correctness and implement a minimal fix
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F39760&comment_hash=6062995249c9b4e64e382e196067055f791f27a2502255170719a8f1d172c25a&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F39760&comment_hash=6062995249c9b4e64e382e196067055f791f27a2502255170719a8f1d172c25a&reaction=dislike'>👎</a>



##########
superset-frontend/plugins/plugin-chart-cartodiagram/src/util/transformPropsUtil.ts:
##########
@@ -230,39 +298,167 @@ export const stripGeomColumnFromLabelMap = (
  * @returns query data without geom column.
  */
 export const stripGeomColumnFromQueryData = (
-  queryData: any,
+  queryData: TimeseriesChartDataResponseResult,
   geomColumn: string,
+  geomFormat: GeometryFormat,
 ) => {
   const queryDataClone = {
     ...structuredClone(queryData),
-    ...stripGeomFromColnamesAndTypes(queryData, geomColumn),
+    ...stripGeomFromColnamesAndTypes(queryData, geomColumn, geomFormat),
   };
   if (queryDataClone.label_map) {
     queryDataClone.label_map = stripGeomColumnFromLabelMap(
       queryData.label_map,
       geomColumn,
+      geomFormat,
     );
   }
   return queryDataClone;
 };
 
+// copy of
+// superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
+export const formatSeriesName = (
+  name: DataRecordValue | undefined,
+  {
+    numberFormatter,
+    timeFormatter,
+    coltype,
+  }: {
+    numberFormatter?: ValueFormatter;
+    timeFormatter?: TimeFormatter;
+    coltype?: GenericDataType;
+  } = {},
+) => {
+  if (name === undefined || name === null) {
+    return NULL_STRING;
+  }
+  if (typeof name === 'boolean' || typeof name === 'bigint') {
+    return name.toString();
+  }
+  if (name instanceof Date || coltype === GenericDataType.Temporal) {
+    const normalizedName =
+      typeof name === 'string' ? normalizeTimestamp(name) : name;
+    const d =
+      normalizedName instanceof Date
+        ? normalizedName
+        : new Date(normalizedName);
+
+    return timeFormatter ? timeFormatter(d) : d.toISOString();
+  }
+  if (typeof name === 'number') {
+    return numberFormatter ? numberFormatter(name) : name.toString();
+  }
+  return name;
+};
+
+// copy of
+// superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
+export const extractGroupbyLabel = ({
+  datum = {},
+  groupby,
+  numberFormatter,
+  timeFormatter,
+  coltypeMapping = {},
+}: {
+  datum?: DataRecord;
+  groupby?: string[] | null;
+  numberFormatter?: NumberFormatter;
+  timeFormatter?: TimeFormatter;
+  coltypeMapping?: Record<string, GenericDataType>;
+}) =>
+  ensureIsArray(groupby)
+    .map(val =>
+      formatSeriesName(datum[val], {
+        numberFormatter,
+        timeFormatter,
+        ...(coltypeMapping[val] && { coltype: coltypeMapping[val] }),
+      }),
+    )
+    .join(', ');
+
+// copy of
+// superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
+export const getColtypesMapping = ({
+  coltypes = [],
+  colnames = [],
+}: Pick<ChartDataResponseResult, 'coltypes' | 'colnames'>): Record<
+  string,
+  GenericDataType
+> =>
+  colnames.reduce(
+    (accumulator, item, index) => ({ ...accumulator, [item]: coltypes[index] 
}),
+    {},
+  );
+
+/**
+ * Reserve label colors for the chart.
+ *
+ * We call the CategoricalColorNamespace singleton to reserve
+ * label colors for the chart. This is necessary to ensure that
+ * we do not run into color collisions when rendering multiple
+ * charts in the same cartodiagram.
+ *
+ * TODO This only works in the context of a dashboard,
+ *      since only there, the CategoricalColorNamespace singleton is being 
used.
+ *      In the explore view, label colors cannot be reserved without changing
+ *      the overall color handling in superset.
+ *
+ * @param formData The formdata of the underlying chart
+ * @param dataByLocation The data grouped by location
+ * @param strippedQueryData The query data without the geom column
+ * @param sliceId The id of the chart slice
+ */
+export const reserveLabelColors = (
+  formData: Record<string, any>,
+  dataByLocation: LocationConfigMapping,
+  strippedQueryData: any,
+  sliceId: number,
+) => {
+  // Call color singleton to reserve label colors
+  // get needed control values from underlying chart config
+  const { colorScheme = '', groupby = [], dateFormat } = formData;
+  const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
+  const groupbyLabels = groupby.map(getColumnLabel);
+  Object.keys(dataByLocation).forEach(location => {

Review Comment:
   **Suggestion:** `groupby` can be `null` in Superset form data, and this code 
calls `.map` on it directly, which will throw at runtime. Normalize `groupby` 
with an array guard (for example via `ensureIsArray`) before mapping so color 
reservation does not crash chart rendering. [possible bug]
   
   <details>
   <summary><b>Severity Level:</b> Critical 🚨</summary>
   
   ```mdx
   - ❌ Cartodiagram fails when underlying chart has null groupby.
   - ⚠️ Dashboard render may show blank or error state.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Render a cartodiagram visualization that references another Superset 
chart whose saved
   form-data has `groupby` explicitly set to `null` (not `undefined`), which is 
passed
   through `selectedChart.params` into `getChartConfigs` in
   
`superset-frontend/plugins/plugin-chart-cartodiagram/src/util/transformPropsUtil.ts:451-458`.
   
   2. In `getChartConfigs`, `chartFormData` is created from 
`selectedChart.params` and stored
   in `baseConfig.formData` (`transformPropsUtil.ts:459-463`), then
   `reserveLabelColors(baseConfig.formData, dataByLocation, strippedQueryData, 
sliceId)` is
   called at `transformPropsUtil.ts:507-512`.
   
   3. Inside `reserveLabelColors` (`transformPropsUtil.ts:412-438`), 
destructuring at line
   421 assigns `groupby = null` because the default `[]` only applies when the 
property is
   `undefined`, not `null`.
   
   4. The subsequent line `groupby.map(getColumnLabel)` at 
`transformPropsUtil.ts:423`
   executes `map` on `null`, throwing `TypeError: groupby.map is not a 
function`, which
   aborts `reserveLabelColors` and `getChartConfigs`, preventing the 
cartodiagram plugin’s
   transformProps from completing and resulting in the map failing to render.
   ```
   </details>
   
   [![Fix in 
Cursor](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-cursor-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=cf2dfc7a2c3545599ee0afda7a96ef63&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 [![Fix in VSCode 
Claude](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-vscode-claude-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=cf2dfc7a2c3545599ee0afda7a96ef63&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
   
   *(Use Cmd/Ctrl + Click for best experience)*
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/plugin-chart-cartodiagram/src/util/transformPropsUtil.ts
   **Line:** 421:423
   **Comment:**
        *Possible Bug: `groupby` can be `null` in Superset form data, and this 
code calls `.map` on it directly, which will throw at runtime. Normalize 
`groupby` with an array guard (for example via `ensureIsArray`) before mapping 
so color reservation does not crash chart rendering.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   Once fix is implemented, also check other comments on the same PR, and ask 
user if the user wants to fix the rest of the comments as well. if said yes, 
then fetch all the comments validate the correctness and implement a minimal fix
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F39760&comment_hash=342d37d35c0765747ce98ab71b34c6b180d39546e199b592d6cb70b5a407553e&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F39760&comment_hash=342d37d35c0765747ce98ab71b34c6b180d39546e199b592d6cb70b5a407553e&reaction=dislike'>👎</a>



##########
superset-frontend/plugins/plugin-chart-cartodiagram/src/util/transformPropsUtil.ts:
##########
@@ -95,16 +160,16 @@ export const groupByLocationGenericX = (
           return;
         }
 
-        const geojsonCols = getGeojsonColumns(labelMap);
+        const geomColumns = getGeomColumns(geomFormat, labelMap);
 
-        if (geojsonCols.length > 1) {
+        if (geomColumns.length > 1) {
           // TODO what should we do, if there is more than one geom column?
           console.log(
             'More than one geometry column detected. Using first found.',
           );
         }
-        const location = labelMap[geojsonCols[0]];
-        const filter = geojsonCols.length ? [geojsonCols[0]] : [];
+        const location = labelMap[geomColumns[0]];
+        const filter = geomColumns.length ? [geomColumns[0]] : [];
         const leftOverKey = createColumnName(labelMap, filter);

Review Comment:
   **Suggestion:** When no geometry column is detected, this indexes `labelMap` 
with `undefined`, which creates an invalid location key and can later cascade 
into geometry parsing failures. Add an explicit `geomColumns.length === 0` 
branch to skip or safely handle records with no detected geometry. [logic error]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ❌ Data grouped under invalid 'undefined' geometry location.
   - ⚠️ Downstream geometry parsing fails for such locations.
   ```
   </details>
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Use a referenced chart whose `label_map` entries for a non-`x_axis` key 
contain no
   values detectable as geometry by `getGeomColumns(geomFormat, labelMap)` (for 
example, a
   metric column only), and render a cartodiagram with generic x-axis 
configuration so
   `groupByLocationGenericX` is used (`transformPropsUtil.ts:140-149`).
   
   2. In `groupByLocationGenericX` (`transformPropsUtil.ts:140-225`), for such 
a key `k`,
   `labelMap` is taken from `queryData.label_map[k]` at lines 154-155, and 
`geomColumns =
   getGeomColumns(geomFormat, labelMap)` at line 163 returns an empty array.
   
   3. With `geomColumns.length === 0`, `location = labelMap[geomColumns[0]]` at 
line 171
   yields `location === undefined`, while `filter` becomes `[]` at line 172 and 
`leftOverKey`
   is computed from the full `labelMap` at line 173.
   
   4. The code then uses `locations[location]` at lines 175-176, creating a 
location key
   `'undefined'`; later, in `getChartConfigs` (`transformPropsUtil.ts:480-549`),
   `Object.keys(dataByLocation)` is iterated, and each `location` string is 
treated as
   geometry (parsed via JSON/WKB/WKT in `getChartConfigs` at lines 525-537), so 
the
   `'undefined'` key leads to invalid geometry parsing and can cause runtime 
errors or
   missing features in the resulting FeatureCollection.
   ```
   </details>
   
   [![Fix in 
Cursor](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-cursor-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=cursor&prompt_id=40da8fcb931249389b98961e11cdbc09&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
 [![Fix in VSCode 
Claude](https://new-codeant-butcket.s3.us-west-1.amazonaws.com/badges/fix-in-vscode-claude-flat.svg)](https://app.codeant.ai/fix-in-ide?tool=vscode-claude&prompt_id=40da8fcb931249389b98961e11cdbc09&service=github&base_url=https%3A%2F%2Fgithub.com&org=apache&repo=apache%2Fsuperset)
   
   *(Use Cmd/Ctrl + Click for best experience)*
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/plugins/plugin-chart-cartodiagram/src/util/transformPropsUtil.ts
   **Line:** 171:173
   **Comment:**
        *Logic Error: When no geometry column is detected, this indexes 
`labelMap` with `undefined`, which creates an invalid location key and can 
later cascade into geometry parsing failures. Add an explicit 
`geomColumns.length === 0` branch to skip or safely handle records with no 
detected geometry.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   Once fix is implemented, also check other comments on the same PR, and ask 
user if the user wants to fix the rest of the comments as well. if said yes, 
then fetch all the comments validate the correctness and implement a minimal fix
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F39760&comment_hash=c0a4ce073249cf0a1aff7e325c92c3a6c32f2dc6cd368c429f400060236b53dc&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F39760&comment_hash=c0a4ce073249cf0a1aff7e325c92c3a6c32f2dc6cd368c429f400060236b53dc&reaction=dislike'>👎</a>



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to