<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: ArcGIS 4.32 operators now returning curve geometries - how to convert to normal polygons? in ArcGIS JavaScript Maps SDK Questions</title>
    <link>https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/arcgis-4-32-operators-now-returning-curve/m-p/1590001#M86603</link>
    <description>&lt;P&gt;My current work around is to send it through the densifyOperator. This conceptually makes sense I guess, but does feel a bit undocumented?&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;The complete code is here for reference:&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;import * as densifyOperator from '@arcgis/core/geometry/operators/densifyOperator.js';
import * as minimumBoundingCircleOperator from '@arcgis/core/geometry/operators/minimumBoundingCircleOperator.js';
import * as projectOperator from '@arcgis/core/geometry/operators/projectOperator.js';
import { useDebouncedValue } from '@mantine/hooks';
import React, { type JSX } from 'react';

import { useArcState } from '@/features/arcgis/hooks';
import { LineGraphic } from '@/types';
import { isDefined } from '@/types/typeGuards';

type Point = [number, number];

export interface SvgPreviewOptions {
  rotation?: number;
  size?: number;
  padding?: number;
  bgColor?: string;
  lineColor?: string;
  lineWidth?: number;
}

/**
 * Scales and centers polyline points to fit within a circular viewport
 * @param polyline - Input polyline geometry
 * @param size - SVG viewport size
 * @param padding - Padding inside circle
 * @returns Normalized points scaled to fit viewport
 */
function normalizePoints(polyline: __esri.Polyline, size: number, padding: number): Point[][] {
  console.log(minimumBoundingCircleOperator.supportsCurves);
  const boundingCircle = minimumBoundingCircleOperator.execute(polyline);
  const boundingPolygonCircle = densifyOperator.execute(boundingCircle, 100) as __esri.Polygon;
  const { extent, centroid } = boundingPolygonCircle;
  if (!isDefined(centroid) || !isDefined(extent)) {
    return [];
  }

  const radius = extent.width / 2;
  const availableRadius = size / 2 - padding;
  const scale = radius === 0 ? 1 : availableRadius / radius;

  return polyline.paths.map((path) =&amp;gt; {
    return path.map(([x, y]) =&amp;gt; [
      ((x ?? 0) - centroid.x) * scale + size / 2,
      size - (((y ?? 0) - centroid.y) * scale + size / 2),
    ]);
  });
}

/**
 * Converts array of point paths to SVG path data strings
 */
function generatePathData(paths: Point[][]): string[] {
  return paths.map((path) =&amp;gt;
    path.map((point, index) =&amp;gt; `${index === 0 ? 'M' : 'L'} ${point[0]},${point[1]}`).join(' '),
  );
}

function PolylineSVG({
  polyline,
  options,
}: {
  polyline: __esri.Polyline;
  options: SvgPreviewOptions;
}): JSX.Element {
  const {
    rotation = 0,
    size = 100,
    padding = 10,
    bgColor = '#f0f0f0',
    lineColor = '#000000',
    lineWidth = 2,
  } = options;

  const normalizedPoints = normalizePoints(polyline, size, padding);
  const pathData = generatePathData(normalizedPoints);

  return (
    &amp;lt;svg
      width={size}
      height={size}
      viewBox={`0 0 ${size} ${size}`}
      xmlns="http://www.w3.org/2000/svg"
      style={{
        transform: `rotate(${rotation}deg)`,
      }}
    &amp;gt;
      &amp;lt;circle cx={size / 2} cy={size / 2} r={size / 2} fill={bgColor} /&amp;gt;
      {pathData.map((path, index) =&amp;gt; (
        &amp;lt;path
          key={index}
          d={path}
          stroke={lineColor}
          strokeWidth={lineWidth}
          fill="none"
          strokeLinecap="round"
          strokeLinejoin="round"
        /&amp;gt;
      ))}
    &amp;lt;/svg&amp;gt;
  );
}

export function MapGraphicPolylinePreviewSVG({
  mapView,
  graphic,
  options,
}: {
  mapView: __esri.MapView;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  graphic: LineGraphic&amp;lt;any&amp;gt;;
  options: SvgPreviewOptions;
}): JSX.Element {
  const [mapRotation] = useArcState(mapView, 'rotation');
  const [geometry] = useArcState(graphic, 'geometry');

  const [debouncedGeometry] = useDebouncedValue(geometry, 50);
  const projectedPolyline = React.useMemo(
    () =&amp;gt; projectOperator.execute(debouncedGeometry, mapView.spatialReference) as __esri.Polyline,
    [debouncedGeometry, mapView.spatialReference],
  );

  return (
    &amp;lt;PolylineSVG
      polyline={projectedPolyline ?? debouncedGeometry}
      options={{ ...options, rotation: mapRotation }}
    /&amp;gt;
  );
}&lt;/LI-CODE&gt;</description>
    <pubDate>Thu, 27 Feb 2025 12:41:01 GMT</pubDate>
    <dc:creator>JonathanDawe_BAS</dc:creator>
    <dc:date>2025-02-27T12:41:01Z</dc:date>
    <item>
      <title>ArcGIS 4.32 operators now returning curve geometries - how to convert to normal polygons?</title>
      <link>https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/arcgis-4-32-operators-now-returning-curve/m-p/1589951#M86601</link>
      <description>&lt;P&gt;I've recently upgraded to ArcGIS JS API version 4.32. After the update I've noticed that some of the geometry operators now return true curves.&amp;nbsp;&lt;/P&gt;&lt;P&gt;For example the `&lt;SPAN&gt;minimumBoundingCircleOperator` now returns a polygon with curved rings. This is breaking my logic as I was previously using the centroid and extent of the polygon which now showing as null in the geometry.&lt;/SPAN&gt;&lt;/P&gt;&lt;P&gt;Is there a way of flagging that I want to produce normal geometries? Currently I can't see much guidance of how to work with curved rings etc.&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Thu, 27 Feb 2025 11:57:41 GMT</pubDate>
      <guid>https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/arcgis-4-32-operators-now-returning-curve/m-p/1589951#M86601</guid>
      <dc:creator>JonathanDawe_BAS</dc:creator>
      <dc:date>2025-02-27T11:57:41Z</dc:date>
    </item>
    <item>
      <title>Re: ArcGIS 4.32 operators now returning curve geometries - how to convert to normal polygons?</title>
      <link>https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/arcgis-4-32-operators-now-returning-curve/m-p/1590001#M86603</link>
      <description>&lt;P&gt;My current work around is to send it through the densifyOperator. This conceptually makes sense I guess, but does feel a bit undocumented?&amp;nbsp;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;The complete code is here for reference:&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;import * as densifyOperator from '@arcgis/core/geometry/operators/densifyOperator.js';
import * as minimumBoundingCircleOperator from '@arcgis/core/geometry/operators/minimumBoundingCircleOperator.js';
import * as projectOperator from '@arcgis/core/geometry/operators/projectOperator.js';
import { useDebouncedValue } from '@mantine/hooks';
import React, { type JSX } from 'react';

import { useArcState } from '@/features/arcgis/hooks';
import { LineGraphic } from '@/types';
import { isDefined } from '@/types/typeGuards';

type Point = [number, number];

export interface SvgPreviewOptions {
  rotation?: number;
  size?: number;
  padding?: number;
  bgColor?: string;
  lineColor?: string;
  lineWidth?: number;
}

/**
 * Scales and centers polyline points to fit within a circular viewport
 * @param polyline - Input polyline geometry
 * @param size - SVG viewport size
 * @param padding - Padding inside circle
 * @returns Normalized points scaled to fit viewport
 */
function normalizePoints(polyline: __esri.Polyline, size: number, padding: number): Point[][] {
  console.log(minimumBoundingCircleOperator.supportsCurves);
  const boundingCircle = minimumBoundingCircleOperator.execute(polyline);
  const boundingPolygonCircle = densifyOperator.execute(boundingCircle, 100) as __esri.Polygon;
  const { extent, centroid } = boundingPolygonCircle;
  if (!isDefined(centroid) || !isDefined(extent)) {
    return [];
  }

  const radius = extent.width / 2;
  const availableRadius = size / 2 - padding;
  const scale = radius === 0 ? 1 : availableRadius / radius;

  return polyline.paths.map((path) =&amp;gt; {
    return path.map(([x, y]) =&amp;gt; [
      ((x ?? 0) - centroid.x) * scale + size / 2,
      size - (((y ?? 0) - centroid.y) * scale + size / 2),
    ]);
  });
}

/**
 * Converts array of point paths to SVG path data strings
 */
function generatePathData(paths: Point[][]): string[] {
  return paths.map((path) =&amp;gt;
    path.map((point, index) =&amp;gt; `${index === 0 ? 'M' : 'L'} ${point[0]},${point[1]}`).join(' '),
  );
}

function PolylineSVG({
  polyline,
  options,
}: {
  polyline: __esri.Polyline;
  options: SvgPreviewOptions;
}): JSX.Element {
  const {
    rotation = 0,
    size = 100,
    padding = 10,
    bgColor = '#f0f0f0',
    lineColor = '#000000',
    lineWidth = 2,
  } = options;

  const normalizedPoints = normalizePoints(polyline, size, padding);
  const pathData = generatePathData(normalizedPoints);

  return (
    &amp;lt;svg
      width={size}
      height={size}
      viewBox={`0 0 ${size} ${size}`}
      xmlns="http://www.w3.org/2000/svg"
      style={{
        transform: `rotate(${rotation}deg)`,
      }}
    &amp;gt;
      &amp;lt;circle cx={size / 2} cy={size / 2} r={size / 2} fill={bgColor} /&amp;gt;
      {pathData.map((path, index) =&amp;gt; (
        &amp;lt;path
          key={index}
          d={path}
          stroke={lineColor}
          strokeWidth={lineWidth}
          fill="none"
          strokeLinecap="round"
          strokeLinejoin="round"
        /&amp;gt;
      ))}
    &amp;lt;/svg&amp;gt;
  );
}

export function MapGraphicPolylinePreviewSVG({
  mapView,
  graphic,
  options,
}: {
  mapView: __esri.MapView;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  graphic: LineGraphic&amp;lt;any&amp;gt;;
  options: SvgPreviewOptions;
}): JSX.Element {
  const [mapRotation] = useArcState(mapView, 'rotation');
  const [geometry] = useArcState(graphic, 'geometry');

  const [debouncedGeometry] = useDebouncedValue(geometry, 50);
  const projectedPolyline = React.useMemo(
    () =&amp;gt; projectOperator.execute(debouncedGeometry, mapView.spatialReference) as __esri.Polyline,
    [debouncedGeometry, mapView.spatialReference],
  );

  return (
    &amp;lt;PolylineSVG
      polyline={projectedPolyline ?? debouncedGeometry}
      options={{ ...options, rotation: mapRotation }}
    /&amp;gt;
  );
}&lt;/LI-CODE&gt;</description>
      <pubDate>Thu, 27 Feb 2025 12:41:01 GMT</pubDate>
      <guid>https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/arcgis-4-32-operators-now-returning-curve/m-p/1590001#M86603</guid>
      <dc:creator>JonathanDawe_BAS</dc:creator>
      <dc:date>2025-02-27T12:41:01Z</dc:date>
    </item>
    <item>
      <title>Re: ArcGIS 4.32 operators now returning curve geometries - how to convert to normal polygons?</title>
      <link>https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/arcgis-4-32-operators-now-returning-curve/m-p/1590274#M86604</link>
      <description>&lt;P&gt;Hi &lt;a href="https://community.esri.com/t5/user/viewprofilepage/user-id/833978"&gt;@JonathanDawe_BAS&lt;/a&gt;&amp;nbsp; thanks for the feedback. Yes, the correct approach is to use the &lt;A href="https://developers.arcgis.com/javascript/latest/api-reference/esri-geometry-operators-densifyOperator.html" target="_self"&gt;densifyOperator&lt;/A&gt; to remove curves from the output geometries at version 4.32+. There's also a code snippet in the densifyOperator API reference:&lt;/P&gt;&lt;LI-CODE lang="javascript"&gt;// Returns a polyline where all curve segments have been densified.
if (polyline.curvePaths) {
  const densified = densifyOperator.execute(polyline, 0, { maxAngleInDegrees: 1 });
}&lt;/LI-CODE&gt;&lt;P&gt;We'll add more clarification to the minimumBoundingCircleOperator documentation that it returns a polygon with curves, even when using input geometries that don't have curves.&lt;/P&gt;</description>
      <pubDate>Thu, 27 Feb 2025 19:09:45 GMT</pubDate>
      <guid>https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/arcgis-4-32-operators-now-returning-curve/m-p/1590274#M86604</guid>
      <dc:creator>AndyGup</dc:creator>
      <dc:date>2025-02-27T19:09:45Z</dc:date>
    </item>
    <item>
      <title>Re: ArcGIS 4.32 operators now returning curve geometries - how to convert to normal polygons?</title>
      <link>https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/arcgis-4-32-operators-now-returning-curve/m-p/1590595#M86606</link>
      <description>&lt;P&gt;Thanks Andy, that makes sense I missed that code snippet! I'll update my logic in this case to densify by angle, rather than using a max segment length as this makes more sense for my use case.&amp;nbsp;&lt;/P&gt;&lt;P&gt;My other feedback would be that the documentation for the densify operator is really hard to read. A lot of the sentences in the overview are really hard to parse and could be rewritten in more plain english.&amp;nbsp;&lt;/P&gt;&lt;P&gt;For example:&amp;nbsp;&lt;/P&gt;&lt;BLOCKQUOTE&gt;&lt;P&gt;When the maxAngleInDegrees is not zero, vertices are introduced at points on the curve where the included angle between tangents at those point is maxAngleInDegrees. The new vertices are connected with line segments.&lt;/P&gt;&lt;P&gt;&lt;SPAN&gt;If&amp;nbsp;&lt;/SPAN&gt;maxSegmentLength&lt;SPAN&gt;&amp;nbsp;is zero, the operation only applies to curves and geometries that do not have curves will be passed through unchanged. The operation always starts from the highest point on segment, thus guaranteeing that geometries that equal segments are densified exactly the same.&lt;/SPAN&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;P&gt;Could become:&lt;/P&gt;&lt;BLOCKQUOTE&gt;&lt;P&gt;If &lt;STRONG&gt;maxAngleInDegrees&lt;/STRONG&gt; is not zero, &lt;STRONG&gt;vertices&lt;/STRONG&gt; are added at points along the curve where the angle between tangent segments reaches this value. These vertices are then connected by straight-line segments.&lt;/P&gt;&lt;P&gt;If &lt;STRONG&gt;maxSegmentLength&lt;/STRONG&gt; is zero, only curves are affected. Other geometries remain unchanged. The process always starts from the highest point on a segment, so identical segments will be densified in the same way.&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;P&gt;Cheers for the help!&lt;/P&gt;</description>
      <pubDate>Fri, 28 Feb 2025 09:48:17 GMT</pubDate>
      <guid>https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/arcgis-4-32-operators-now-returning-curve/m-p/1590595#M86606</guid>
      <dc:creator>JonathanDawe_BAS</dc:creator>
      <dc:date>2025-02-28T09:48:17Z</dc:date>
    </item>
    <item>
      <title>Re: ArcGIS 4.32 operators now returning curve geometries - how to convert to normal polygons?</title>
      <link>https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/arcgis-4-32-operators-now-returning-curve/m-p/1590810#M86608</link>
      <description>&lt;P&gt;Good catch on the wording, I'll blend your suggestions in. We agree that some of the wording could use "fine tuning". The first phase was to get all 60+ new operators into the API, now we are in the enhancement phase.&lt;/P&gt;</description>
      <pubDate>Fri, 28 Feb 2025 17:24:17 GMT</pubDate>
      <guid>https://community.esri.com/t5/arcgis-javascript-maps-sdk-questions/arcgis-4-32-operators-now-returning-curve/m-p/1590810#M86608</guid>
      <dc:creator>AndyGup</dc:creator>
      <dc:date>2025-02-28T17:24:17Z</dc:date>
    </item>
  </channel>
</rss>

