بنقرة واحدة
migrate-map
Migrate Leaflet maps from legacy MVC to Angular using neptune-map and layer components.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Migrate Leaflet maps from legacy MVC to Angular using neptune-map and layer components.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Create or flesh out a Jira story with structured sections for POs, Devs, and Claude. Accepts an optional Jira key to update an existing card.
Export Water Quality Management Plans for one jurisdiction from the local Neptune database to an Esri File Geodatabase in native projection. Use when a client requests WQMP spatial data for a specific city/jurisdiction.
Extract rework items from a Jira card's Acceptance Criteria and produce a fix plan. Auto-detects issue key from branch or accepts one as argument.
Fetch a Jira card by key (e.g. NPT-100) and produce a detailed implementation plan. Use this when starting work on a new story.
Build the SQL Server DACPAC and scaffold EF Core entities. Run this after finishing all SQL schema edits in Neptune.Database/.
Add a scrollspy Table of Contents sidebar to a page component with signal-based scroll tracking.
| name | migrate-map |
| description | Migrate Leaflet maps from legacy MVC to Angular using neptune-map and layer components. |
| allowed-tools | ["Read","Glob","Grep","Edit","Write","Bash(dotnet build:*)","Bash(npm run gen-model:*)"] |
| argument-hint | <EntityName> |
When the user invokes /migrate-map <EntityName>:
This skill guides the migration of Leaflet maps from legacy MVC views to Angular using the neptune-map component and associated layer components.
First, examine the legacy MVC implementation:
Neptune.WebMvc/Views/{Entity}/
<div id="*Map*"> or similar map containersleaflet or map-related classesNeptune.WebMvc/Scripts/ for map initialization
L.map(), L.geoJSON(), L.tileLayer() callsNeptune.WebMvc/Controllers/{Entity}Controller.cs
For entity boundaries or spatial data, create endpoints returning FeatureCollection:
// In EntityController.cs
[HttpGet("{entityID}/boundary")]
[UserViewFeature]
public ActionResult<FeatureCollection> GetBoundary([FromRoute] int entityID)
{
var features = Entities.GetBoundaryAsFeatureCollection(DbContext, entityID);
return Ok(features);
}
// In Entities.cs
public static FeatureCollection GetBoundaryAsFeatureCollection(
NeptuneDbContext dbContext, int entityID)
{
var entity = dbContext.Entities
.AsNoTracking()
.Where(x => x.EntityID == entityID && x.EntityGeometry != null)
.Select(x => new { x.EntityID, x.EntityGeometry })
.SingleOrDefault();
if (entity?.EntityGeometry == null)
return new FeatureCollection();
var feature = new Feature(entity.EntityGeometry, new AttributesTable
{
{ "EntityID", entity.EntityID }
});
return new FeatureCollection(new[] { feature });
}
Add boolean flags to the detail DTO to indicate if map data exists:
// In EntityDto.cs
public bool HasBoundary { get; set; }
public BoundingBoxDto? BoundingBox { get; set; }
| Component | Selector | Purpose |
|---|---|---|
NeptuneMapComponent | neptune-map | Base map container |
DelineationsLayerComponent | delineations-layer | BMP delineation polygons |
JurisdictionsLayerComponent | jurisdictions-layer | Stormwater jurisdiction boundaries |
LandUseBlockLayerComponent | land-use-block-layer | Land use block polygons |
InventoriedBmpsLayerComponent | inventoried-bmps-layer | Inventoried BMP locations |
OvtaAreaLayerComponent | ovta-area-layer | Single OVTA area |
OvtaAreasLayerComponent | ovta-areas-layer | Multiple OVTA areas |
ParcelLayerComponent | parcel-layer | Parcels |
PermitTypeLayerComponent | permit-type-layer | Permit type boundaries |
TrashGeneratingUnitLayerComponent | trash-generating-unit-layer | Trash generating units |
StormwaterNetworkLayerComponent | stormwater-network-layer | Stormwater network |
WqmpsLayerComponent | wqmps-layer | Water quality management plans |
RegionalSubbasinsLayerComponent | regional-subbasins-layer | Regional subbasins |
LoadGeneratingUnitsLayerComponent | load-generating-units-layer | Load generating units |
GenericWmsWfsLayerComponent | generic-wms-wfs-layer | Generic WMS/WFS with OverlayMode |
import { OverlayMode } from "src/app/shared/components/leaflet/layers/generic-wms-wfs-layer/overlay-mode.enum";
import { Component } from "@angular/core";
import { Observable } from "rxjs";
import { Map } from "leaflet";
import { NeptuneMapComponent } from "src/app/shared/components/leaflet/neptune-map/neptune-map.component";
import { DelineationsLayerComponent } from "src/app/shared/components/leaflet/layers/delineations-layer/delineations-layer.component";
import { JurisdictionsLayerComponent } from "src/app/shared/components/leaflet/layers/jurisdictions-layer/jurisdictions-layer.component";
@Component({
// ...
imports: [
NeptuneMapComponent,
DelineationsLayerComponent,
JurisdictionsLayerComponent,
// other layer components as needed
],
})
export class EntityDetailComponent {
public map: Map;
public layerControl: L.Control.Layers;
public mapIsReady: boolean = false;
handleMapReady(event: MapInitEvent): void {
this.map = event.map;
this.layerControl = event.layerControl;
this.mapIsReady = true;
}
}
@if (entity.HasBoundary) {
<div class="card">
<div class="card-header"><span class="card-title">Map</span></div>
<div class="card-body">
<neptune-map
[mapHeight]="'400px'"
[boundingBox]="entity.BoundingBox"
(onMapLoad)="handleMapReady($event)">
<!-- Delineation layer -->
@if (mapIsReady) {
<delineations-layer
[map]="map"
[layerControl]="layerControl"
[treatmentBMPID]="entity.TreatmentBMPID">
</delineations-layer>
}
<!-- Jurisdiction reference layer -->
@if (mapIsReady) {
<jurisdictions-layer
[map]="map"
[layerControl]="layerControl">
</jurisdictions-layer>
}
</neptune-map>
</div>
</div>
}
Neptune uses vGeoServer* views for WMS/WFS layers served by GeoServer. Common views:
vGeoServerTreatmentBMP — BMP locationsvGeoServerDelineation — BMP delineation polygonsvGeoServerTrashGeneratingUnit — Trash generating unitsvGeoServerLandUseBlock — Land use blocksvGeoServerOnlandVisualTrashAssessmentArea — OVTA areasvGeoServerRegionalSubbasin — Regional subbasinsvGeoServerWaterQualityManagementPlan — WQMPsvGeoServerStormwaterNetwork — Stormwater network| Input | Type | Default | Description |
|---|---|---|---|
mapHeight | string | '500px' | CSS height of map container |
selectedTileLayer | string | 'Terrain' | Default base layer ('Terrain', 'Street', 'Satellite') |
boundingBox | BoundingBoxDto | Orange County, CA | Initial bounds |
showLegend | boolean | false | Show legend control |
disableMapInteraction | boolean | false | Lock map (no pan/zoom) |
collapseLayerControlOnLoad | boolean | false | Start with layers control collapsed |
| Output | Type | Description |
|---|---|---|
onMapLoad | MapInitEvent | Fired when map is ready; provides map and layerControl |
<div class="grid-12">
<div class="g-col-6">
<!-- Map card -->
</div>
<div class="g-col-6">
<!-- Details card -->
</div>
</div>
<div class="grid-12">
<div class="g-col-12">
<!-- Map card -->
</div>
<div class="g-col-12">
<!-- Grid card -->
</div>
</div>
@if (entity.HasBoundary) {
<div class="card">
<div class="card-header"><span class="card-title">Location</span></div>
<div class="card-body">
<neptune-map ...>
<!-- layers -->
</neptune-map>
</div>
</div>
} @else {
<div class="card">
<div class="card-header"><span class="card-title">Location</span></div>
<div class="card-body">
<p class="text-muted">No location data available for this entity.</p>
</div>
</div>
}
HasBoundary / spatial flags to detail DTOBoundingBox to detail DTO (if applicable)neptune-map component to component importshandleMapReady methodnpm run gen-model after API changeshandleMapReady is called and sets mapIsReady = true@if (mapIsReady) guardmap and layerControl are passed to layer components| async)mapHeight inputboundingBox input to map component