Skip to content

Map: OSM provider - Add route calculation and rendering - #35108

Merged
AlisherAmonulloev merged 14 commits into
feature/26_2_osm-provider-for-dxmap/mainfrom
feature/26_2_osm-provider-for-dxmap/route-calculation-and-rendering
Sep 10, 2026
Merged

Map: OSM provider - Add route calculation and rendering#35108
AlisherAmonulloev merged 14 commits into
feature/26_2_osm-provider-for-dxmap/mainfrom
feature/26_2_osm-provider-for-dxmap/route-calculation-and-rendering

Conversation

@AlisherAmonulloev

Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Failed route calculations currently create phantom routes and fire misleading route-added events.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds callback-driven OSM route calculation and OpenLayers vector rendering, including projection-aware bounds and antimeridian handling.

Changes:

  • Adds route parsing, rendering, styling, lifecycle handling, and viewport fitting.
  • Registers required OpenLayers vector APIs.
  • Expands unit tests, mocks, and Storybook coverage.
File summaries
File Description
osmTests.js Tests OSM route behavior and lifecycle.
openLayersMock.js Mocks vector layers, features, geometry, and styles.
openlayers.register.js Registers OpenLayers vector dependencies.
provider.dynamic.osm.ts Implements route calculation and provider integration.
provider.dynamic.osm.test.ts Tests route-aware bounds.
provider.dynamic.osm.route.ts Parses routes and normalizes longitudes.
provider.dynamic.osm.route.test.ts Tests route parsing and longitude handling.
provider.dynamic.osm.openlayers.utils.ts Extends OpenLayers interfaces and validation.
provider.dynamic.osm.openlayers.utils.test.ts Updates API validation fixtures.
provider.dynamic.osm.openlayers.ts Renders and disposes vector routes.
provider.dynamic.osm.engine.ts Adds route contracts to the engine API.
routes.ts Provides static Storybook route data.
OSMMap.stories.tsx Demonstrates configurable OSM routes.
Review details
  • Files reviewed: 13/13 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/devextreme/js/__internal/ui/map/provider.dynamic.osm.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Remove-only updates leave auto-adjusted viewports stale, and missing routing configuration can produce duplicate warnings.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 13/13 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread packages/devextreme/js/__internal/ui/map/provider.dynamic.osm.ts Outdated
Comment thread packages/devextreme/js/__internal/ui/map/provider.dynamic.osm.ts
@AlisherAmonulloev
AlisherAmonulloev marked this pull request as ready for review September 9, 2026 11:04
@AlisherAmonulloev
AlisherAmonulloev requested a review from a team as a code owner September 9, 2026 11:04

@EugeniyKiyashko EugeniyKiyashko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

14. Minor - general comment. packages/devextreme/js/ui/map.d.ts:409 is not part of this diff, so this one cannot be anchored to a line.

calculateRoute?: (params: CalculateOsmRouteInfo) => PromiseLike<OsmRouteResult> does not allow undefined, although the implementation and the tests support "no result", while calculateLocation right next to it does allow undefined. The implementation also accepts a plain, non-promise return value, which the type does not. Worth aligning to OsmRouteResult | undefined and possibly allowing a non-promise return.

Comment thread packages/devextreme/js/__internal/ui/map/provider.dynamic.osm.ts
Comment thread packages/devextreme/js/__internal/ui/map/provider.dynamic.osm.ts Outdated
Comment thread packages/devextreme/js/__internal/ui/map/provider.dynamic.osm.ts Outdated
Comment on lines +558 to +562
return this._renderRoute(options).then((routeObject) => {
if (routeObject.engineRoute) {
this._routes.push({ ...routeObject, options });
this._fireRouteAddedAction({ options, originalRoute: routeObject.instance });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4. Important. A failed route is not stored in _routes and fires neither onRouteAdded nor onRouteRemoved, but it stays in option('routes'). This is better than google, which stores a dummy object and raises onRouteAdded anyway, and the choice is deliberate and covered by tests. Two consequences, though:

  • a visible difference in public behaviour between providers;
  • option('routes') and what is actually drawn diverge, and the application only learns about it from the console.

This needs an explicit line in the provider documentation. It is also worth discussing whether onRouteAdded should fire with originalRoute: undefined, so an application can react to the skip.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree that this should be documented, will notify the doc team

Comment thread packages/devextreme/js/__internal/ui/map/provider.dynamic.osm.ts
Comment on lines +177 to +181
if (previousProjection !== projection) {
this._routeSource?.getFeatures().forEach((feature) => {
feature.getGeometry()?.transform(previousProjection, projection);
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10. Minor. The geometry is transformed in place. Markers recover on their own because their position is recomputed from latitude and longitude every time, but routes do not: calling setUserProjection() after routes have been added leaves them in the old coordinate system, and there is no change:view event in that case.

Keeping the original waypoints on the route handle and rebuilding the geometry would be more robust. It would also avoid accumulating error from repeated transforms: latitude of 90 degrees, which getRouteLocations allows, is clamped to about 85.05 in EPSG:3857.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently, we assume that the global user projection is configured before map initialization
a separate sync mechanism will is needed if we allow changing projection at runtime, keeping the original coordinates would not solve this case. Let's keep this as is and wait for user feedback

const locations: MapLocation[] = [];
for (const coordinate of coordinates) {
if (!Array.isArray(coordinate)
|| (isGeoJson ? coordinate.length < 2 : coordinate.length !== 2)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11. Minor. The two formats are checked with different strictness: GeoJSON positions may carry extra components (length of at least two, elevation ignored), while tuples must have exactly two. Because of that, a result made of [lat, lng, elevation] tuples drops the whole route. One rule for both would be more predictable - either both tolerate extra components, or neither does.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

our tuple contract is [lat, lng], and GeoJSON allows a third altitude coordinate, we do not use it
let's keep these checks and document the coordinate order and altitude handling :)

Comment thread apps/react-storybook/stories/map/OSMMap.stories.tsx Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The implementation is cohesive and supported by extensive validation, lifecycle, projection, and integration coverage.

Review details
  • Files reviewed: 16/16 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@AlisherAmonulloev

Copy link
Copy Markdown
Contributor Author

14. Minor - general comment. packages/devextreme/js/ui/map.d.ts:409 is not part of this diff, so this one cannot be anchored to a line.

calculateRoute?: (params: CalculateOsmRouteInfo) => PromiseLike<OsmRouteResult> does not allow undefined, although the implementation and the tests support "no result", while calculateLocation right next to it does allow undefined. The implementation also accepts a plain, non-promise return value, which the type does not. Worth aligning to OsmRouteResult | undefined and possibly allowing a non-promise return.

we handle undefined defensively, but it is not a supported callback result.
I suggest keeping PromiseLike<OsmRouteResult>: for instance, our self-hosted OSRM returns HTTP 400 with NoRoute, and the callback rejects its promise. The provider catches this, logs W1006, and skips the route without breaking the map.

Synchronous results are also tolerated internally, but this does not need to expand the public contract. Unlike calculateLocation, we have not defined undefined as a normal “not found” result for calculateRoute.

@AlisherAmonulloev
AlisherAmonulloev merged commit 538f332 into feature/26_2_osm-provider-for-dxmap/main Sep 10, 2026
99 checks passed
@AlisherAmonulloev
AlisherAmonulloev deleted the feature/26_2_osm-provider-for-dxmap/route-calculation-and-rendering branch September 10, 2026 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants