diff --git a/.dockerignore b/.dockerignore index 984bfffbf..a6f75bbdd 100644 --- a/.dockerignore +++ b/.dockerignore @@ -19,11 +19,14 @@ **/node_modules **/npm-debug.log **/obj +**/esBuild.lock bin obj **/wwwroot/js/*.js +**/wwwroot/js/*.js.map **/secrets.dev.yaml **/values.dev.yaml +**/.esbuild-record.json test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp.Client/wwwroot/appsettings.json LICENSE README.md \ No newline at end of file diff --git a/src/.editorconfig b/.editorconfig similarity index 100% rename from src/.editorconfig rename to .editorconfig diff --git a/.github/workflows/dev-pr-build.yml b/.github/workflows/dev-pr-build.yml index 505e73619..12088bbb3 100644 --- a/.github/workflows/dev-pr-build.yml +++ b/.github/workflows/dev-pr-build.yml @@ -59,8 +59,16 @@ jobs: HTTP_PORT: 8082 HTTPS_PORT: 9445 run: | - dotnet test --project ./test/dymaptic.GeoBlazor.Core.Test.Automation/dymaptic.GeoBlazor.Core.Test.Automation.csproj -c Release --filter CORE_ - + dotnet ./build-tools/win-x64/GBTest.dll -c Release --cover --container --filter Core + + - name: Upload test log + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-run-log + retention-days: 4 + path: ./test/dymaptic.GeoBlazor.Core.Test.Automation/test-run.log + build: runs-on: ubuntu-latest needs: [ actor-check ] @@ -99,7 +107,7 @@ jobs: - name: Build GeoBlazor shell: pwsh run: | - dotnet ./build-tools/GeoBlazorBuild.dll -xml -pkg -docs -c "Release" + dotnet ./build-tools/linux-x64/GeoBlazorBuild.dll -xml -pkg -docs -c "Release" # Copies the nuget package to the artifacts directory - name: Upload nuget artifact diff --git a/.github/workflows/main-release-build.yml b/.github/workflows/main-release-build.yml index 26910c343..fec69211d 100644 --- a/.github/workflows/main-release-build.yml +++ b/.github/workflows/main-release-build.yml @@ -44,13 +44,21 @@ jobs: HTTP_PORT: 8082 HTTPS_PORT: 9445 run: | - dotnet test --project ./test/dymaptic.GeoBlazor.Core.Test.Automation/dymaptic.GeoBlazor.Core.Test.Automation.csproj -c Release --filter CORE_ - + dotnet ./build-tools/win-x64/GBTest.dll -c Release --container --filter Core + + - name: Upload test log + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-run-log + retention-days: 4 + path: ./test/dymaptic.GeoBlazor.Core.Test.Automation/test-run.log + # This runs the main GeoBlazor build script - name: Build GeoBlazor shell: pwsh run: | - dotnet ./build-tools/GeoBlazorBuild.dll -xml -pkg -pub -c "Release" + dotnet ./build-tools/win-x64/GeoBlazorBuild.dll -xml -pkg -pub -c "Release" # xmllint is a dependency of the copy steps below - name: Install xmllint diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6c4291b76..7510a9bc3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -43,4 +43,12 @@ jobs: HTTP_PORT: 8082 HTTPS_PORT: 9445 run: | - dotnet test --project ./test/dymaptic.GeoBlazor.Core.Test.Automation/dymaptic.GeoBlazor.Core.Test.Automation.csproj -c Release --filter CORE_ \ No newline at end of file + dotnet ./build-tools/win-x64/GBTest.dll -c Release --cover --container --filter Core + + - name: Upload test log + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-run-log + retention-days: 4 + path: ./test/dymaptic.GeoBlazor.Core.Test.Automation/test-run.log \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1fd10f24f..ad4500f0f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,15 +10,19 @@ *.userosscache *.sln.docstates .DS_Store -esBuild.*.lock +**/esBuild.lock esBuild.log +esBuild.hash +*.binlog .esbuild-record.json +.csbuild-record.json CustomerTests.razor .claude/ .env test/dymaptic.GeoBlazor.Core.Test.Automation/test.txt test/dymaptic.GeoBlazor.Core.Test.Automation/test-run.log test/dymaptic.GeoBlazor.Core.Test.Automation/coverage* +test/dymaptic.GeoBlazor.Core.Test.Automation/history* test/dymaptic.GeoBlazor.Core.Test.Automation/unit-coverage* test/dymaptic.GeoBlazor.Core.Test.Automation/sgen-coverage* test/dymaptic.GeoBlazor.Core.Test.Automation/Summary.txt diff --git a/CLAUDE.md b/CLAUDE.md index e971b0a6a..a220b2340 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,7 +38,7 @@ GeoBlazor is a Blazor component library that brings ArcGIS Maps SDK for JavaScri ### Build ```bash # Clean build of the Core project -dotnet ./build-tools/GeoBlazorBuild.dll +dotnet ./build-tools/win-x64/GeoBlazorBuild.dll -- _`GeoBlazorBuild` includes lots of options, use -h to see options_ @@ -77,7 +77,7 @@ pwsh bumpVersion.ps1 -test 1.2.3 # Test version bump ### Development ```bash # Clear ESBuild locks if build is stuck -dotnet ./build-tools/ESBuildClearLocks.dll +dotnet ./build-tools/win-x64/ESBuildClearLocks.dll # Watch TypeScript changes (from src/dymaptic.GeoBlazor.Core/) npm run watchBuild diff --git a/Directory.Build.props b/Directory.Build.props index a17678e13..949a79009 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -5,24 +5,76 @@ enable enable - 5.0.0.38 true + 5.0.0.75 Debug;Release;SourceGen Highlighting AnyCPU - $(MSBuildThisFileDirectory)src\dymaptic.GeoBlazor.Core + true + $(MSBuildThisFileDirectory)src/dymaptic.GeoBlazor.Core $(StaticWebAssetEndpointExclusionPattern);js/** + + $(MSBuildThisFileDirectory)build-tools/win-x64 + + + $(MSBuildThisFileDirectory)build-tools/win-arm64 + + + $(MSBuildThisFileDirectory)build-tools/osx-x64 + + + $(MSBuildThisFileDirectory)build-tools/osx-arm64 + + + $(MSBuildThisFileDirectory)build-tools/linux-x64 + + + $(MSBuildThisFileDirectory)build-tools/linux-arm64 + + - - - + + + + + + TriggerESBuild;$(BuildDependsOn) + $(MSBuildThisFileDirectory)src/dymaptic.GeoBlazor.Core/esBuild.lock + $([System.IO.File]::GetLastWriteTime(`$(ESBuildLockFilePath)`).Ticks) + 0 + $([System.DateTime]::Now.AddMinutes(-5).Ticks) + + BuildProjectReferences; + Configuration; + EnableDefaultCompileItems; + EnableDefaultEmbeddedResourceItems; + EnableDefaultNoneItems; + ExcludeRestorePackageImports; + GBBuildToolsPath; + MSBuildIsRestoring; + MSBuildRestoreSessionId; + NugetInteractive; + Platform; + ProESBuildRequired; + ShowScriptDialogs; + TargetFramework; + TargetFrameworks; + TriggerESBuild; + TriggerProESBuild + + + + + + + \ No newline at end of file diff --git a/Directory.Build.targets b/Directory.Build.targets deleted file mode 100644 index ff2f4fea7..000000000 --- a/Directory.Build.targets +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 832b91c9a..ace78ff5a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,12 @@ +# syntax=docker/dockerfile:1.7.0 FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build ARG ARCGIS_API_KEY ARG GEOBLAZOR_LICENSE_KEY ARG WFS_SERVERS ARG HTTP_PORT ARG HTTPS_PORT -ENV ARCGIS_API_KEY=${ARCGIS_API_KEY} -ENV GEOBLAZOR_LICENSE_KEY=${GEOBLAZOR_LICENSE_KEY} -ENV WFS_SERVERS=${WFS_SERVERS} +# Install NodeJS and NPM RUN apt-get update \ && apt-get install -y ca-certificates curl gnupg \ && mkdir -p /etc/apt/keyrings \ @@ -16,51 +15,63 @@ RUN apt-get update \ && apt-get update \ && apt-get install -y nodejs +# Install NPM Packages WORKDIR /work WORKDIR /work/src/dymaptic.GeoBlazor.Core COPY ./src/dymaptic.GeoBlazor.Core/package.json ./package.json -RUN npm install +RUN --mount=type=cache,target=/root/.npm npm install WORKDIR /work -COPY ./src/ ./src/ + +# Update GeoBlazor Build Scripts +COPY ./build-tools/build-scripts ./build-tools/build-scripts +COPY ./build-tools/utilities ./build-tools/utilities +RUN --mount=type=cache,target=/root/.nuget/packages \ + dotnet run ./build-tools/build-scripts/ScriptBuilder.cs + +# Copy Source Files COPY ./*.ps1 ./ COPY ./Directory.Build.* ./ COPY ./.gitignore ./.gitignore COPY ./nuget.config ./nuget.config -COPY ./build-tools ./build-tools -COPY ./build-scripts/ScriptBuilder.cs ./build-scripts/ScriptBuilder.cs - -RUN dotnet ./build-tools/GeoBlazorBuild.dll -v current - -COPY ./test/dymaptic.GeoBlazor.Core.Test.Blazor.Shared/dymaptic.GeoBlazor.Core.Test.Blazor.Shared.csproj ./test/dymaptic.GeoBlazor.Core.Test.Blazor.Shared.csproj -COPY ./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp.csproj ./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp.csproj -COPY ./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp.Client/dymaptic.GeoBlazor.Core.Test.WebApp.Client.csproj ./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp.Client/dymaptic.GeoBlazor.Core.Test.WebApp.Client.csproj - -# Use UsePackageReference=false to build from source (enables code coverage with PDB symbols) -RUN dotnet restore ./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp.csproj /p:UsePackageReference=false - +COPY ./src/ ./src/ COPY ./test/dymaptic.GeoBlazor.Core.Test.Blazor.Shared ./test/dymaptic.GeoBlazor.Core.Test.Blazor.Shared COPY ./test/dymaptic.GeoBlazor.Core.Test.WebApp ./test/dymaptic.GeoBlazor.Core.Test.WebApp -RUN dotnet ./build-tools/BuildAppSettings.dll \ - -k "$ARCGIS_API_KEY" \ - -l "$GEOBLAZOR_LICENSE_KEY" \ +# Create appsettings files +RUN dotnet ./build-tools/linux-x64/BuildAppSettings.dll \ + -k "${ARCGIS_API_KEY}" \ + -l "${GEOBLAZOR_LICENSE_KEY}" \ -o "./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp.Client/wwwroot/appsettings.json" \ -o "./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp.Client/wwwroot/appsettings.Production.json" \ -o "./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp/appsettings.json" \ -o "./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp/appsettings.Production.json" \ - -w "$WFS_SERVERS" + -w "${WFS_SERVERS}" # Build from source with debug symbols for code coverage # UsePackageReference=false builds GeoBlazor from source instead of NuGet # DebugSymbols=true and DebugType=portable ensure PDB files are generated -RUN dotnet publish ./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp.csproj \ +RUN --mount=type=cache,target=/root/.nuget/packages \ + dotnet build ./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp.csproj \ + -c Release \ + /p:UsePackageReference=false \ + /p:DebugSymbols=true \ + /p:DebugType=portable \ + /p:GeneratePackage=false \ + /p:GenerateDocs=false \ + /p:GenerateXmlComments=false \ + /p:ShowScriptDialogs=false + +RUN --mount=type=cache,target=/root/.nuget/packages \ + dotnet publish ./test/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp/dymaptic.GeoBlazor.Core.Test.WebApp.csproj \ -c Release \ /p:UsePackageReference=false \ - /p:PipelineBuild=true \ /p:DebugSymbols=true \ /p:DebugType=portable \ - /p:GeneratePack=false \ + /p:GeneratePackage=false \ + /p:GenerateDocs=false \ + /p:GenerateXmlComments=false \ + /p:ShowScriptDialogs=false \ -o /app/publish FROM mcr.microsoft.com/dotnet/aspnet:10.0 @@ -109,6 +120,7 @@ ENV ASPNETCORE_Kestrel__Certificates__Default__Password=password # Coverage configuration (can be overridden via environment) ENV COVERAGE_ENABLED=false ENV COVERAGE_FORMAT=xml +ENV SESSION_ID=WEB_APP # Copy entrypoint script COPY ./test/dymaptic.GeoBlazor.Core.Test.Automation/docker-entrypoint.sh /docker-entrypoint.sh @@ -117,4 +129,4 @@ RUN chmod +x /docker-entrypoint.sh USER info EXPOSE ${HTTP_PORT} ${HTTPS_PORT} ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["dotnet", "dymaptic.GeoBlazor.Core.Test.WebApp.dll"] +CMD ["dotnet", "dymaptic.GeoBlazor.Core.Test.WebApp.dll"] \ No newline at end of file diff --git a/Dockerfile.unit b/Dockerfile.unit new file mode 100644 index 000000000..89e4afbbc --- /dev/null +++ b/Dockerfile.unit @@ -0,0 +1,86 @@ +# syntax=docker/dockerfile:1.7.0 +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +ENV DOTNET_CLI_HOME=/tmp/dotnet-cli + +# Generate a self-signed certificate for HTTPS and install bash for entrypoint script +# Also install libxml2 which is required for dotnet-coverage profiler +RUN apt-get update && apt-get install -y --no-install-recommends libxml2 gnupg \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /unit-coverage + +# Install dotnet-coverage tool to a shared location accessible by all users +RUN mkdir -p /tools && \ + /usr/share/dotnet/dotnet tool install --tool-path /tools dotnet-coverage && \ + chmod -R 755 /tools + +# Install NodeJS and NPM +RUN apt-get update \ + && apt-get install -y ca-certificates curl gnupg \ + && mkdir -p /etc/apt/keyrings \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ + && apt-get update \ + && apt-get install -y nodejs + +# Install NPM Packages +WORKDIR /work +WORKDIR /work/src/dymaptic.GeoBlazor.Core +COPY ./src/dymaptic.GeoBlazor.Core/package.json ./package.json +RUN --mount=type=cache,target=/root/.npm npm install + +WORKDIR /work + +# Update GeoBlazor Build Scripts +COPY ./build-tools/build-scripts ./build-tools/build-scripts +COPY ./build-tools/utilities ./build-tools/utilities +RUN --mount=type=cache,target=/root/.nuget/packages \ + dotnet run ./build-tools/build-scripts/ScriptBuilder.cs + +# Input variables +ARG TEST_GROUP=UNIT +ARG TEST_FOLDER +ARG TEST_PROJECT +ENV TEST_GROUP=${TEST_GROUP} +ENV TEST_PROJECT=${TEST_PROJECT} + +# Copy Source Files +COPY ./nuget.config ./nuget.config +COPY ./Directory.Build.* ./ +COPY ./src/ ./src/ +COPY ./*.ps1 ./ +COPY ./.gitignore ./.gitignore +COPY $TEST_FOLDER $TEST_FOLDER + +# Build test app +RUN --mount=type=cache,target=/root/.nuget/packages \ + dotnet build $TEST_PROJECT \ + -c Release \ + /p:UsePackageReference=false \ + /p:DebugSymbols=true \ + /p:DebugType=portable \ + /p:GeneratePackage=false \ + /p:GenerateDocs=false \ + /p:GenerateXmlComments=false \ + /p:ShowScriptDialogs=false + +# Create user and set working directory +RUN groupadd -r info && useradd -r -g info info \ + && chown -R info:info /unit-coverage \ + && mkdir -p /tmp/dotnet-cli \ + && chown -R info:info /tmp/dotnet-cli \ + && chown -R info:info /work/test + +# Copy entrypoint script +COPY ./test/dymaptic.GeoBlazor.Core.Test.Automation/docker-entrypoint.sh /docker-entrypoint.sh +RUN chmod +x /docker-entrypoint.sh + +ARG FILTER +ENV FILTER=${FILTER} +# Coverage configuration (can be overridden via environment) +ENV COVERAGE_ENABLED=false +ENV COVERAGE_FORMAT=xml +ENV SESSION_ID=WEB_APP +ENV CONTAINER_CHECK=false + +USER info +ENTRYPOINT ["/bin/bash", "/docker-entrypoint.sh"] \ No newline at end of file diff --git a/badge_fullmethodcoverage.svg b/badge_fullmethodcoverage.svg index f80e7c08e..fee221371 100644 --- a/badge_fullmethodcoverage.svg +++ b/badge_fullmethodcoverage.svg @@ -132,7 +132,7 @@ - 3%3% + 29.7%29.7% diff --git a/badge_linecoverage.svg b/badge_linecoverage.svg index 24ecd11fc..1f8c96aaa 100644 --- a/badge_linecoverage.svg +++ b/badge_linecoverage.svg @@ -123,7 +123,7 @@ Coverage Coverage - 1.3%1.3% + 9.2%9.2% diff --git a/badge_methodcoverage.svg b/badge_methodcoverage.svg index f06ecb44c..b182c1557 100644 --- a/badge_methodcoverage.svg +++ b/badge_methodcoverage.svg @@ -125,7 +125,7 @@ Coverage - 3.2%3.2% + 31.9%31.9% diff --git a/build-scripts/ConsoleDialog.cs b/build-scripts/ConsoleDialog.cs deleted file mode 100644 index 1fca2a591..000000000 --- a/build-scripts/ConsoleDialog.cs +++ /dev/null @@ -1,276 +0,0 @@ -#!/usr/bin/env dotnet - -using System.Diagnostics; - -// Manages a console window for displaying log messages during source generation. - -object _consoleLock = new(); -Process? _consoleProcess = null; -string? _consoleTempFile = null; - -string? title = null; -int wait = 3; - -for (int i = 0; i < args.Length; i++) -{ - string arg = args[i]; - - switch (arg) - { - case "-w": - case "--wait": - wait = int.TryParse(args[i + 1], out int parsedWait) ? parsedWait : wait; - i++; - break; - default: - if (title is null) - { - title = arg; - } - else - { - title = $"{title} {arg}"; - } - break; - } -} - -title ??= "GeoBlazor Build"; - -void ShowOrUpdateConsole(string title, string message) -{ - lock (_consoleLock) - { - // Ensure the temp file exists (create if needed) - if (_consoleTempFile is null || !File.Exists(_consoleTempFile)) - { - _consoleTempFile = Path.Combine(Path.GetTempPath(), $"geoblazor_sourcegen_{Guid.NewGuid():N}.log"); - // Create the file immediately so Get-Content -Wait has something to tail - File.WriteAllText(_consoleTempFile, $" {Environment.NewLine}"); - } - - if (!string.IsNullOrWhiteSpace(message)) - { - // Append message to the temp file - string timestamp = DateTime.Now.ToString("HH:mm:ss"); - string logLine = $"[{timestamp}] {title}: {message}{Environment.NewLine}"; - File.AppendAllText(_consoleTempFile, logLine); - } - - // Start the console window if not already running - if (_consoleProcess is null || _consoleProcess.HasExited) - { - StartConsoleWindow(title); - } - } -} - -void StartConsoleWindow(string title) -{ - try - { - if (OperatingSystem.IsWindows()) - { - StartWindowsConsole(title); - } - else if (OperatingSystem.IsMacOS()) - { - StartMacConsole(); - } - else if (OperatingSystem.IsLinux()) - { - StartLinuxConsole(); - } - } - catch - { - // Console window creation failed - continue silently - // Messages are still written to the temp file and MSBuild diagnostics - } -} - -void StartWindowsConsole(string title) -{ - string escapedPath = _consoleTempFile!.Replace("'", "''"); - string windowTitle = string.IsNullOrWhiteSpace(title) ? "GeoBlazor Build" : title; - string command = $"$Host.UI.RawUI.WindowTitle = '{windowTitle}'; " + - $"Write-Host 'GeoBlazor Source Generator Output' -ForegroundColor Cyan; " + - $"Write-Host ('=' * 50); " + - $"Get-Content -Path '{escapedPath}' -Wait -Tail 100"; - - _consoleProcess = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = "pwsh", - Arguments = $"-NoProfile -NoLogo -Command \"{command}\"", - UseShellExecute = true, - CreateNoWindow = false - } - }; - _consoleProcess.Start(); -} - -void StartMacConsole() -{ - string escapedPath = _consoleTempFile!.Replace("'", "'\\''"); - - // Use osascript to open Terminal.app with a pwsh command - string pwshCommand = "pwsh -NoProfile -NoLogo -Command \\\"" + - "Write-Host 'GeoBlazor Source Generator Output' -ForegroundColor Cyan; " + - "Write-Host ('=' * 50); " + - $"Get-Content -Path '{escapedPath}' -Wait -Tail 100\\\""; - - string appleScript = $"tell application \\\"Terminal\\\" to do script \\\"{pwshCommand}\\\""; - - _consoleProcess = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = "osascript", - Arguments = $"-e \"{appleScript}\"", - UseShellExecute = false, - CreateNoWindow = true - } - }; - _consoleProcess.Start(); -} - -void StartLinuxConsole() -{ - string escapedPath = _consoleTempFile!.Replace("'", "'\\''"); - string pwshCommand = "pwsh -NoProfile -NoLogo -Command \\\"" + - "Write-Host 'GeoBlazor Source Generator Output' -ForegroundColor Cyan; " + - "Write-Host ('=' * 50); " + - $"Get-Content -Path '{escapedPath}' -Wait -Tail 100\\\""; - - // Try common Linux terminal emulators in order of popularity - string[] terminals = ["gnome-terminal", "konsole", "xfce4-terminal", "xterm"]; - - foreach (string terminal in terminals) - { - try - { - string args = terminal switch - { - "gnome-terminal" => $"-- bash -c \"{pwshCommand}\"", - "konsole" => $"-e bash -c \"{pwshCommand}\"", - "xfce4-terminal" => $"-e \"bash -c \\\"{pwshCommand}\\\"\"", - "xterm" => $"-e bash -c \"{pwshCommand}\"", - _ => $"-e bash -c \"{pwshCommand}\"" - }; - - _consoleProcess = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = terminal, - Arguments = args, - UseShellExecute = false, - CreateNoWindow = true - } - }; - _consoleProcess.Start(); - return; // Success, exit the loop - } - catch - { - // This terminal emulator not available, try the next one - } - } - - // No terminal emulator found - messages still go to temp file and diagnostics -} - -void CloseConsole(string title, int wait) -{ - lock (_consoleLock) - { - try - { - if (_consoleProcess is { HasExited: false } && _consoleTempFile is not null) - { - File.WriteAllText(_consoleTempFile, $"[{DateTime.Now:HH:mm:ss}] {title}: Console closing..."); - // Give a brief moment for final messages to appear - Thread.Sleep(wait * 1000); - _consoleProcess.Kill(); - _consoleProcess.Dispose(); - } - } - catch - { - // Process may have already exited - } - finally - { - _consoleProcess = null; - } - - // Clean up temp file - try - { - if (_consoleTempFile is not null && File.Exists(_consoleTempFile)) - { - File.Delete(_consoleTempFile); - } - } - catch - { - // File may be locked - ignore - } - finally - { - _consoleTempFile = null; - } - } -} - -ShowOrUpdateConsole(title, string.Empty); - -CancellationTokenSource cts = new(); -cts.CancelAfter(TimeSpan.FromSeconds(60)); - -bool hold = false; - -_ = Task.Run(async () => -{ - while ((!cts.IsCancellationRequested || hold) - && (_consoleProcess is null || !_consoleProcess.HasExited)) - { - await Task.Delay(1000); - } - Console.WriteLine("Console dialog timed out. Closing..."); - CloseConsole(title, wait); - Environment.Exit(0); -}); - -while (!cts.IsCancellationRequested) -{ - if (_consoleProcess?.HasExited == true) - { - break; - } - - if (Console.ReadLine() is not { } inputLine) - { - continue; - } - - if (inputLine.Trim().Equals("hold", StringComparison.OrdinalIgnoreCase)) - { - hold = true; - - break; - } - - if (inputLine.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase)) - { - CloseConsole(title, wait); - - break; - } - - ShowOrUpdateConsole(title, inputLine); -} - -Environment.Exit(0); \ No newline at end of file diff --git a/build-scripts/ESBuildWaitForCompletion.cs b/build-scripts/ESBuildWaitForCompletion.cs deleted file mode 100644 index a76c72a29..000000000 --- a/build-scripts/ESBuildWaitForCompletion.cs +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env dotnet - -// ESBuild Wait For Completion Script -// C# file-based app version of esBuildWaitForCompletion.ps1 -// Waits for ESBuild lock files to be released before proceeding -// -// Usage: dotnet ESBuildWaitForCompletion.cs [options] -// -c, --configuration Build configuration (default: Debug) -// -t, --timeout Timeout in seconds (default: 30) -// -h, --help Display help message -// -// Example: -// dotnet ESBuildWaitForCompletion.cs -// dotnet ESBuildWaitForCompletion.cs -c Release -// dotnet ESBuildWaitForCompletion.cs -c Debug -t 60 - -using System.Runtime.CompilerServices; - -// Parse command line arguments -string configuration = "Debug"; -int timeout = 30; -bool help = false; - -for (int i = 0; i < args.Length; i++) -{ - string arg = args[i].ToLowerInvariant(); - switch (arg) - { - case "-c": - case "--configuration": - if (i + 1 < args.Length) - { - configuration = args[++i]; - } - break; - case "-t": - case "--timeout": - if (i + 1 < args.Length && int.TryParse(args[++i], out int t)) - { - timeout = t; - } - break; - case "-h": - case "--help": - help = true; - break; - } -} - -if (help) -{ - Console.WriteLine("ESBuild Wait For Completion Script"); - Console.WriteLine("Waits for ESBuild lock files to be released before proceeding."); - Console.WriteLine(); - Console.WriteLine("Usage: dotnet ESBuildWaitForCompletion.cs [options]"); - Console.WriteLine(); - Console.WriteLine("Options:"); - Console.WriteLine(" -c, --configuration Build configuration (default: Debug)"); - Console.WriteLine(" -t, --timeout Timeout in seconds (default: 30)"); - Console.WriteLine(" -h, --help Display this help message"); - Console.WriteLine(); - Console.WriteLine("Examples:"); - Console.WriteLine(" dotnet ESBuildWaitForCompletion.cs"); - Console.WriteLine(" dotnet ESBuildWaitForCompletion.cs -c Release"); - Console.WriteLine(" dotnet ESBuildWaitForCompletion.cs -c Debug -t 60"); - return 0; -} - -// Normalize configuration -configuration = configuration.Equals("release", StringComparison.OrdinalIgnoreCase) ? "Release" : "Debug"; - -string scriptDir = GetScriptDirectory(); - -// Define paths relative to script location (build-scripts folder) -// Core: ../src/dymaptic.GeoBlazor.Core -// Pro: ../../src/dymaptic.GeoBlazor.Pro -string coreRootDir = Path.GetFullPath(Path.Combine(scriptDir, "..", "src", "dymaptic.GeoBlazor.Core")); -string proRootDir = Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "src", "dymaptic.GeoBlazor.Pro")); - -string coreLockFilePath = Path.Combine(coreRootDir, $"esBuild.{configuration}.lock"); -string proLockFilePath = Path.Combine(proRootDir, $"esBuild.{configuration}.lock"); - -Console.WriteLine($"Waiting for lock files: {coreLockFilePath}, {proLockFilePath}"); - -bool coreLockExists = File.Exists(coreLockFilePath); -bool proLockExists = File.Exists(proLockFilePath); - -if (coreLockExists || proLockExists) -{ - Console.WriteLine("Lock file found. Waiting for release."); -} -else -{ - Console.WriteLine("No lock file found. Exiting."); - return 0; -} - -int elapsed = 0; - -while (File.Exists(coreLockFilePath) || File.Exists(proLockFilePath)) -{ - Thread.Sleep(1000); - Console.Write("."); - elapsed++; - - if (elapsed >= timeout) - { - Console.WriteLine(); - Console.WriteLine($"Timeout reached ({timeout} seconds). Deleting lock files."); - - if (File.Exists(coreLockFilePath)) - { - try - { - File.Delete(coreLockFilePath); - Console.WriteLine($"Deleted: {coreLockFilePath}"); - } - catch (Exception ex) - { - Console.WriteLine($"Failed to delete {coreLockFilePath}: {ex.Message}"); - } - } - - if (File.Exists(proLockFilePath)) - { - try - { - File.Delete(proLockFilePath); - Console.WriteLine($"Deleted: {proLockFilePath}"); - } - catch (Exception ex) - { - Console.WriteLine($"Failed to delete {proLockFilePath}: {ex.Message}"); - } - } - - break; - } -} - -Console.WriteLine(); -Console.WriteLine("Lock file removed. Exiting."); -return 0; - -// Helper method -static string GetScriptDirectory([CallerFilePath] string? callerFilePath = null) -{ - return Path.GetDirectoryName(callerFilePath!) ?? Environment.CurrentDirectory; -} diff --git a/build-scripts/ScriptBuilder.cs b/build-scripts/ScriptBuilder.cs deleted file mode 100644 index 2337cdf8c..000000000 --- a/build-scripts/ScriptBuilder.cs +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env dotnet - -using System.Diagnostics; -using System.Runtime.CompilerServices; - -// Builds all C# scripts in the current directory using dotnet build. -// Outputs built DLLs to ../build-tools/ -// Usage: dotnet ScriptBuilder.cs -// Can also pass in script names to build specific scripts only: -// Usage: dotnet ScriptBuilder.cs Script1.cs Script2.cs ... -// or the --exclude option to skip specific scripts: -// Usage: dotnet ScriptBuilder.cs --exclude Script1.cs Script2.cs ... - -bool excludeMode = false; -HashSet scriptsToProcess = new(); -string scriptDir = GetScriptDirectory(); -string outDir = Path.GetFullPath(Path.Combine(scriptDir, "..", "build-tools")); - -string[] scripts = Directory.GetFiles(scriptDir, "*.cs"); - -for (int i = 0; i < args.Length; i++) -{ - string arg = args[i].ToLowerInvariant(); - - switch (arg) - { - case "--exclude": - excludeMode = true; - break; - default: - scriptsToProcess.Add(arg); - break; - } -} - -foreach (string script in scripts) -{ - if (Path.GetFileName(script) == "ScriptBuilder.cs") - { - continue; - } - - if (scriptsToProcess.Count > 0) - { - if (excludeMode && scriptsToProcess.Contains(Path.GetFileName(script))) - { - Console.WriteLine($"Skipping excluded script: {Path.GetFileName(script)}"); - continue; - } - else if (!excludeMode && !scriptsToProcess.Contains(Path.GetFileName(script))) - { - Console.WriteLine($"Skipping unlisted script: {Path.GetFileName(script)}"); - continue; - } - } - - int returnCode = BuildScript(Path.GetFileName(script), scriptDir, outDir); - if (returnCode != 0) - { - return returnCode; - } -} - -return 0; - - - -static int BuildScript(string scriptName, string scriptDir, string outDir) -{ - string[] args = - [ - "build", - scriptName, - "-c", - "Release", - "-o", - outDir - ]; - - ProcessStartInfo psi = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = string.Join(" ", args), - WorkingDirectory = scriptDir, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false - }; - - using Process? process = Process.Start(psi); - if (process is null) - { - Console.WriteLine($"Failed to build {scriptName}"); - return 1; - } - - // Read output asynchronously - process.OutputDataReceived += (sender, e) => - { - if (e.Data is not null) - { - Console.WriteLine(e.Data); - } - }; - - process.ErrorDataReceived += (sender, e) => - { - if (e.Data is not null) - { - Console.WriteLine(e.Data); - } - }; - - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - process.WaitForExit(); - return process.ExitCode; -} - - -static string GetScriptDirectory([CallerFilePath] string? callerFilePath = null) -{ - return Path.GetDirectoryName(callerFilePath) ?? Environment.CurrentDirectory; -} \ No newline at end of file diff --git a/build-tools/BuildTemplates.dll b/build-tools/BuildTemplates.dll deleted file mode 100644 index 1a0e10405..000000000 Binary files a/build-tools/BuildTemplates.dll and /dev/null differ diff --git a/build-tools/ConsoleDialog.dll b/build-tools/ConsoleDialog.dll deleted file mode 100644 index 14d8c6e52..000000000 Binary files a/build-tools/ConsoleDialog.dll and /dev/null differ diff --git a/build-tools/Directory.Build.props b/build-tools/Directory.Build.props new file mode 100644 index 000000000..0051217de --- /dev/null +++ b/build-tools/Directory.Build.props @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/build-tools/ESBuild.dll b/build-tools/ESBuild.dll deleted file mode 100644 index c51428bb3..000000000 Binary files a/build-tools/ESBuild.dll and /dev/null differ diff --git a/build-tools/ESBuildClearLocks.deps.json b/build-tools/ESBuildClearLocks.deps.json deleted file mode 100644 index 4bfbba92d..000000000 --- a/build-tools/ESBuildClearLocks.deps.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "runtimeTarget": { - "name": ".NETCoreApp,Version=v10.0", - "signature": "" - }, - "compilationOptions": {}, - "targets": { - ".NETCoreApp,Version=v10.0": { - "ESBuildClearLocks/1.0.0": { - "runtime": { - "ESBuildClearLocks.dll": {} - } - } - } - }, - "libraries": { - "ESBuildClearLocks/1.0.0": { - "type": "project", - "serviceable": false, - "sha512": "" - } - } -} \ No newline at end of file diff --git a/build-tools/ESBuildClearLocks.dll b/build-tools/ESBuildClearLocks.dll deleted file mode 100644 index 26bb90e7b..000000000 Binary files a/build-tools/ESBuildClearLocks.dll and /dev/null differ diff --git a/build-tools/ESBuildWaitForCompletion.deps.json b/build-tools/ESBuildWaitForCompletion.deps.json deleted file mode 100644 index 4e271f070..000000000 --- a/build-tools/ESBuildWaitForCompletion.deps.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "runtimeTarget": { - "name": ".NETCoreApp,Version=v10.0", - "signature": "" - }, - "compilationOptions": {}, - "targets": { - ".NETCoreApp,Version=v10.0": { - "ESBuildWaitForCompletion/1.0.0": { - "runtime": { - "ESBuildWaitForCompletion.dll": {} - } - } - } - }, - "libraries": { - "ESBuildWaitForCompletion/1.0.0": { - "type": "project", - "serviceable": false, - "sha512": "" - } - } -} \ No newline at end of file diff --git a/build-tools/ESBuildWaitForCompletion.dll b/build-tools/ESBuildWaitForCompletion.dll deleted file mode 100644 index 38f2439b9..000000000 Binary files a/build-tools/ESBuildWaitForCompletion.dll and /dev/null differ diff --git a/build-tools/ESBuildWaitForCompletion.runtimeconfig.json b/build-tools/ESBuildWaitForCompletion.runtimeconfig.json deleted file mode 100644 index 3e7d1efdf..000000000 --- a/build-tools/ESBuildWaitForCompletion.runtimeconfig.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "runtimeOptions": { - "tfm": "net10.0", - "framework": { - "name": "Microsoft.NETCore.App", - "version": "10.0.0" - }, - "configProperties": { - "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts/ESBuildWaitForCompletion.cs", - "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts", - "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, - "System.ComponentModel.DefaultValueAttribute.IsSupported": false, - "System.ComponentModel.Design.IDesignerHost.IsSupported": false, - "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, - "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, - "System.Data.DataSet.XmlSerializationIsSupported": false, - "System.Diagnostics.Tracing.EventSource.IsSupported": false, - "System.Linq.Enumerable.IsSizeOptimized": true, - "System.Net.SocketsHttpHandler.Http3Support": false, - "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, - "System.Resources.ResourceManager.AllowCustomResourceTypes": false, - "System.Resources.UseSystemResourceKeys": false, - "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, - "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, - "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, - "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, - "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, - "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, - "System.StartupHookProvider.IsSupported": false, - "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, - "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, - "System.Threading.Thread.EnableAutoreleasePool": false, - "System.Linq.Expressions.CanEmitObjectArrayDelegate": false - } - } -} \ No newline at end of file diff --git a/build-tools/GeoBlazorBuild.dll b/build-tools/GeoBlazorBuild.dll deleted file mode 100644 index 2e89f12cb..000000000 Binary files a/build-tools/GeoBlazorBuild.dll and /dev/null differ diff --git a/build-scripts/BuildAppSettings.cs b/build-tools/build-scripts/BuildAppSettings.cs similarity index 100% rename from build-scripts/BuildAppSettings.cs rename to build-tools/build-scripts/BuildAppSettings.cs diff --git a/build-scripts/BuildTemplates.cs b/build-tools/build-scripts/BuildTemplates.cs similarity index 94% rename from build-scripts/BuildTemplates.cs rename to build-tools/build-scripts/BuildTemplates.cs index 13d8a2417..f507e3807 100644 --- a/build-scripts/BuildTemplates.cs +++ b/build-tools/build-scripts/BuildTemplates.cs @@ -1,5 +1,6 @@ #!/usr/bin/env dotnet #:property PublishAot=false +#:project ../utilities/Utilities.csproj // Build Templates Script // C# file-based app version of buildTemplates.ps1 @@ -24,6 +25,7 @@ using System.Text.Json.Nodes; using System.Text.RegularExpressions; using System.Xml.Linq; +using Utilities; // Target frameworks to support int[] targetFrameworks = [8, 9, 10]; @@ -131,10 +133,11 @@ return 0; } -string scriptDir = GetScriptsDirectory(); +string scriptDir = PathFinder.GetScriptsDirectory(); -// Determine templates directory -string templatesDir = templatesDirOverride ?? Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "templates")); +// Scripts are in GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts +// Templates are in GeoBlazor.Pro/templates +string templatesDir = templatesDirOverride ?? Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "..", "templates")); if (!Directory.Exists(templatesDir)) { @@ -429,7 +432,7 @@ Console.WriteLine("Building GeoBlazor Templates project"); string templateProjectPath = Path.Combine(templatesDir, "dymaptic.GeoBlazor.Templates.csproj"); -int restoreResult = RunDotnet($"restore \"{templateProjectPath}\""); +int restoreResult = RunDotnet($"restore \"{templateProjectPath}\" -bl:\"{Path.Combine(templatesDir, "restore.binlog")}\""); if (restoreResult != 0) { Console.Error.WriteLine("Error: dotnet restore failed"); @@ -438,8 +441,7 @@ int buildResult = RunDotnet( $"build \"{templateProjectPath}\" -c Release --no-restore " + - $"/p:ProVersion={proVersion} /p:OptOutFromCoreEsBuild=true " + - "/p:OptOutFromProEsBuild=true /p:GenerateDocs=false /p:GeneratePackage=false"); + $"/p:ProVersion={proVersion} /p:GenerateDocs=false /p:GenerateXmlComments=false /p:GeneratePackage=false -bl:\"{Path.Combine(templatesDir, "build.binlog")}\""); if (buildResult != 0) { @@ -600,20 +602,4 @@ static int RunDotnet(string arguments) process.WaitForExit(); return process.ExitCode; -} - -static string GetScriptsDirectory([CallerFilePath] string? callerFilePath = null) -{ - // When running as a pre-compiled DLL, [CallerFilePath] contains the compile-time path - // which is invalid at runtime (especially in Docker containers). - // Detect this by checking if the file exists at the caller path. - if (!string.IsNullOrEmpty(callerFilePath) && File.Exists(callerFilePath)) - { - return Path.GetDirectoryName(callerFilePath)!; - } - - // Running as a DLL - use AppContext.BaseDirectory which points to the DLL location - // The DLL is in build-tools/, and scripts are in build-scripts/ (sibling directory) - string dllDirectory = AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - return Path.Combine(Path.GetDirectoryName(dllDirectory)!, "build-scripts"); } \ No newline at end of file diff --git a/build-tools/build-scripts/ConsoleDialog.cs b/build-tools/build-scripts/ConsoleDialog.cs new file mode 100644 index 000000000..997e76928 --- /dev/null +++ b/build-tools/build-scripts/ConsoleDialog.cs @@ -0,0 +1,476 @@ +#!/usr/bin/env dotnet + +// Console Dialog - Build Progress Display Window +// =============================================== +// Manages a console window for displaying log messages during source generation +// and build processes. Opens a separate terminal window that tails a log file, +// allowing real-time visibility of build progress. +// +// Usage: +// dotnet ConsoleDialog.cs [title] [options] +// dotnet ConsoleDialog.cs "GeoBlazor Build" Start with custom title +// dotnet ConsoleDialog.cs "Build" -w 5 -t 120 Custom wait/timeout +// +// Options: +// -w, --wait Seconds to wait before closing on exit (default: 3) +// -t, --timeout Idle timeout before auto-close (default: 60) +// +// Communication: +// The dialog reads from stdin. Send lines of text to display in the console window. +// Special commands: +// "hold" - Prevent auto-timeout (keeps window open indefinitely) +// "exit" - Close the console window +// +// Cross-Platform Support: +// - Windows: Opens PowerShell 7 (pwsh) window with Get-Content -Wait +// - macOS: Opens Terminal.app via osascript +// - Linux: Tries gnome-terminal, konsole, xfce4-terminal, or xterm +// +// Note: Messages are written to a temp file and tailed by the console window. + +using System.Diagnostics; + +object _consoleLock = new(); +Process? _consoleProcess = null; +string? _consoleTempFile = null; + +string? title = null; +int wait = 3; +int idleTimeout = 60; + +for (int i = 0; i < args.Length; i++) +{ + string arg = args[i]; + + switch (arg) + { + case "-w": + case "--wait": + wait = int.TryParse(args[i + 1], out int parsedWait) ? parsedWait : wait; + i++; + break; + case "-t": + case "--timeout": + idleTimeout = int.TryParse(args[i + 1], out int parsedTimeout) ? parsedTimeout : idleTimeout; + i++; + break; + default: + if (title is null) + { + title = arg; + } + else + { + title = $"{title} {arg}"; + } + break; + } +} + +title ??= "GeoBlazor Build"; + +/// +/// Shows or updates the console window with a new message. +/// Creates the temp log file and starts the console window on first call. +/// +/// The title for the console window. +/// The message to display (empty string to just ensure window is open). +void ShowOrUpdateConsole(string title, string message) +{ + lock (_consoleLock) + { + // Ensure the temp file exists (create if needed) + if (_consoleTempFile is null || !File.Exists(_consoleTempFile)) + { + _consoleTempFile = Path.Combine(Path.GetTempPath(), $"geoblazor_sourcegen_{Guid.NewGuid():N}.log"); + // Create the file immediately so Get-Content -Wait has something to tail + File.WriteAllText(_consoleTempFile, $" {Environment.NewLine}"); + } + + if (!string.IsNullOrWhiteSpace(message)) + { + // Append message to the temp file + string timestamp = DateTime.Now.ToString("HH:mm:ss"); + string logLine = $"[{timestamp}] {message}{Environment.NewLine}"; + File.AppendAllText(_consoleTempFile, logLine); + } + + // Start the console window if not already running + if (_consoleProcess is null || _consoleProcess.HasExited) + { + StartConsoleWindow(title); + } + } +} + +/// +/// Starts a platform-specific console window that tails the log file. +/// Dispatches to Windows, macOS, or Linux-specific implementations. +/// +/// The title for the console window. +void StartConsoleWindow(string title) +{ + string windowTitle = string.IsNullOrWhiteSpace(title) ? "GeoBlazor Build" : title; + try + { + if (OperatingSystem.IsWindows()) + { + StartWindowsConsole(windowTitle); + } + else if (OperatingSystem.IsMacOS()) + { + StartMacConsole(windowTitle); + } + else if (OperatingSystem.IsLinux()) + { + StartLinuxConsole(windowTitle); + } + } + catch + { + // Console window creation failed - continue silently + // Messages are still written to the temp file and MSBuild diagnostics + } +} + +/// +/// Starts a PowerShell 7 console window on Windows using Get-Content -Wait to tail the log file. +/// +/// The title for the console window. +void StartWindowsConsole(string title) +{ + string escapedPath = _consoleTempFile!.Replace("'", "''"); + string command = $"$Host.UI.RawUI.WindowTitle = '{title}'; " + + $"Write-Host '{title}' -ForegroundColor Cyan; " + + $"Write-Host ('=' * 50); " + + $"Get-Content -Path '{escapedPath}' -Wait -Tail 100"; + + _consoleProcess = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "pwsh", + Arguments = $"-NoProfile -NoLogo -Command \"{command}\"", + UseShellExecute = true, + CreateNoWindow = false + } + }; + _consoleProcess.Start(); +} + +/// +/// Starts a Terminal.app window on macOS using osascript/AppleScript. +/// Uses tail -f for log following (no pwsh dependency required). +/// +void StartMacConsole(string title) +{ + // Escape for single-quoted shell strings + string shellTitle = title.Replace("'", "'\\''"); + string shellPath = _consoleTempFile!.Replace("'", "'\\''"); + + // Build the shell command for the visible Terminal window + // clear removes the login banner and echoed command that Terminal.app shows + string shellCommand = $"clear; echo '{shellTitle}'; echo '=================================================='; tail -f '{shellPath}'"; + + // Escape the shell command for embedding in an AppleScript double-quoted string + string asCommand = shellCommand.Replace("\\", "\\\\").Replace("\"", "\\\""); + + // Pass AppleScript via stdin to avoid nested shell escaping issues + string appleScript = "tell application \"Terminal\"\n" + + " activate\n" + + $" do script \"{asCommand}\"\n" + + "end tell"; + + var osascript = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "osascript", + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardInput = true + } + }; + osascript.Start(); + osascript.StandardInput.Write(appleScript); + osascript.StandardInput.Close(); + osascript.WaitForExit(5000); + + // osascript exits immediately after telling Terminal to open, so start a + // long-lived sentinel process for lifecycle tracking by the main loop + _consoleProcess = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "sleep", + Arguments = "86400", + UseShellExecute = false, + CreateNoWindow = true + } + }; + _consoleProcess.Start(); +} + +/// +/// Starts a terminal window on Linux by trying common terminal emulators +/// (gnome-terminal, konsole, xfce4-terminal, xterm) until one succeeds. +/// Uses tail -f for log following (no pwsh dependency required). +/// +void StartLinuxConsole(string title) +{ + string shellTitle = title.Replace("'", "'\\''"); + string shellPath = _consoleTempFile!.Replace("'", "'\\''"); + + // Shell command to display title banner and follow the log file + string shellCommand = $"echo '{shellTitle}'; echo '=================================================='; tail -f '{shellPath}'"; + + // Try common Linux terminal emulators in order of popularity + // Use ArgumentList to pass args directly as argv, avoiding shell escaping issues + string[] terminals = ["gnome-terminal", "konsole", "xfce4-terminal", "xterm"]; + + foreach (string terminal in terminals) + { + try + { + var startInfo = new ProcessStartInfo + { + FileName = terminal, + UseShellExecute = false, + CreateNoWindow = true + }; + + // Each terminal uses a different flag to specify the command to run + if (terminal == "gnome-terminal") + { + // gnome-terminal uses -- to separate its args from the child command + startInfo.ArgumentList.Add("--"); + } + else + { + // konsole, xfce4-terminal, xterm all use -e + startInfo.ArgumentList.Add("-e"); + } + + startInfo.ArgumentList.Add("bash"); + startInfo.ArgumentList.Add("-c"); + startInfo.ArgumentList.Add(shellCommand); + + var terminalProcess = new Process { StartInfo = startInfo }; + terminalProcess.Start(); + + // gnome-terminal exits immediately (delegates to the GNOME Terminal server), + // so use a sentinel process for lifecycle tracking, consistent with macOS + _consoleProcess = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "sleep", + Arguments = "86400", + UseShellExecute = false, + CreateNoWindow = true + } + }; + _consoleProcess.Start(); + + return; // Success, exit the loop + } + catch + { + // This terminal emulator not available, try the next one + } + } + + // No terminal emulator found - messages still go to temp file and diagnostics +} + +/// +/// Closes the Terminal.app window on macOS by finding and closing the tab +/// that is running tail -f on our temp file. +/// +void CloseMacTerminalWindow(string tempFilePath) +{ + try + { + string escapedPath = tempFilePath.Replace("\\", "\\\\").Replace("\"", "\\\""); + + // AppleScript to find and close the Terminal tab running our tail command + string appleScript = + "tell application \"Terminal\"\n" + + " repeat with w in windows\n" + + " repeat with t in tabs of w\n" + + $" if processes of t contains \"tail\" and history of t contains \"{escapedPath}\" then\n" + + " close w\n" + + " return\n" + + " end if\n" + + " end repeat\n" + + " end repeat\n" + + "end tell"; + + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "osascript", + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardInput = true + } + }; + process.Start(); + process.StandardInput.Write(appleScript); + process.StandardInput.Close(); + process.WaitForExit(5000); + } + catch + { + // Terminal may have already been closed + } +} + +/// +/// Closes a Linux terminal window by killing the tail process that is +/// following our specific temp file. When tail exits, bash -c completes, +/// and the terminal emulator closes the window. +/// +void CloseLinuxTerminalWindow(string tempFilePath) +{ + try + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "pkill", + UseShellExecute = false, + CreateNoWindow = true + } + }; + // Use ArgumentList so the pattern is passed as a single argv element + process.StartInfo.ArgumentList.Add("-f"); + process.StartInfo.ArgumentList.Add($"tail -f {tempFilePath}"); + process.Start(); + process.WaitForExit(5000); + } + catch + { + // Process may have already exited + } +} + +/// +/// Closes the console window gracefully, waiting for final messages to display +/// before killing the process and cleaning up the temp file. +/// +/// The title (used in closing message). +/// Seconds to wait before killing the process. +void CloseConsole(string title, int wait) +{ + lock (_consoleLock) + { + try + { + if (_consoleProcess is { HasExited: false } && _consoleTempFile is not null) + { + File.WriteAllText(_consoleTempFile, $"[{DateTime.Now:HH:mm:ss}] {title}: Console closing..."); + // Give a brief moment for final messages to appear + Thread.Sleep(wait * 1000); + _consoleProcess.Kill(); + _consoleProcess.Dispose(); + } + + // Close the platform-specific terminal window (sentinel process doesn't own it) + if (OperatingSystem.IsMacOS() && _consoleTempFile is not null) + { + CloseMacTerminalWindow(_consoleTempFile); + } + else if (OperatingSystem.IsLinux() && _consoleTempFile is not null) + { + CloseLinuxTerminalWindow(_consoleTempFile); + } + } + catch + { + // Process may have already exited + } + finally + { + _consoleProcess = null; + } + + // Clean up temp file + try + { + if (_consoleTempFile is not null && File.Exists(_consoleTempFile)) + { + File.Delete(_consoleTempFile); + } + } + catch + { + // File may be locked - ignore + } + finally + { + _consoleTempFile = null; + } + } +} + +ShowOrUpdateConsole(title, string.Empty); + +CancellationTokenSource cts = new(); +cts.CancelAfter(TimeSpan.FromSeconds(60)); + +bool hold = false; +bool messageReceived = false; + +_ = Task.Run(async () => +{ + while ((!cts.IsCancellationRequested || hold) + && (_consoleProcess is null || !_consoleProcess.HasExited)) + { + await Task.Delay(1000); + + if (messageReceived) + { + cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromSeconds(idleTimeout)); + messageReceived = false; + } + } + Console.WriteLine("Console dialog timed out. Closing..."); + CloseConsole(title, wait); + Environment.Exit(0); +}); + +while (!cts.IsCancellationRequested) +{ + if (_consoleProcess?.HasExited == true) + { + break; + } + + if (Console.ReadLine() is not { } inputLine) + { + continue; + } + + if (inputLine.Trim().Equals("hold", StringComparison.OrdinalIgnoreCase)) + { + hold = true; + + break; + } + + if (inputLine.Trim().Equals("exit", StringComparison.OrdinalIgnoreCase)) + { + CloseConsole(title, wait); + + break; + } + + messageReceived = true; + ShowOrUpdateConsole(title, inputLine); +} + +Environment.Exit(0); \ No newline at end of file diff --git a/build-tools/build-scripts/Directory.Build.props b/build-tools/build-scripts/Directory.Build.props new file mode 100644 index 000000000..0051217de --- /dev/null +++ b/build-tools/build-scripts/Directory.Build.props @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/build-scripts/ESBuild.cs b/build-tools/build-scripts/ESBuild.cs similarity index 61% rename from build-scripts/ESBuild.cs rename to build-tools/build-scripts/ESBuild.cs index 447e458b8..4e6fe15aa 100644 --- a/build-scripts/ESBuild.cs +++ b/build-tools/build-scripts/ESBuild.cs @@ -1,29 +1,38 @@ #!/usr/bin/env dotnet +#:project ../utilities/Utilities.csproj // ESBuild TypeScript -> JavaScript Compilation Script // C# file-based app version of esBuild.ps1 // Usage: dotnet esBuild.cs [options] // -c, --configuration Build configuration (default: Debug) -// -f, --force Force rebuild, ignoring lock files and record // -p, --pro Run the GeoBlazor Pro ESBuild process +// -pc, --prooncorechange Run the GeoBlazor Pro ESBuild process if pro OR core files have changed // -d, --dialog Show a console dialog during build // -v, --verbose Enable verbose logging // -h, --help Display help message using System.Diagnostics; -using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text.Json; +using System.Text.RegularExpressions; +using Utilities; // Get the actual script location using CallerFilePath (resolved at compile time) -string scriptDir = GetScriptsDirectory(); -string toolsDir = Path.GetFullPath(Path.Combine(scriptDir, "..", "build-tools")); +string scriptDir = PathFinder.GetScriptsDirectory(); +string os = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? "win" + : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) + ? "osx" + : "linux"; +string arch = RuntimeInformation.OSArchitecture.ToString().ToLowerInvariant(); +string toolsDir = Path.GetFullPath(Path.Combine(scriptDir, "..", $"{os}-{arch}")); // Parse command line arguments string configuration = "Debug"; -bool force = false; bool help = false; bool pro = false; +bool proOnCoreChange = false; bool dialog = false; bool verbose = false; @@ -43,14 +52,16 @@ case "--dialog": dialog = true; break; - case "-f": - case "--force": - force = true; - break; case "-p": + case "-pro": case "--pro": pro = true; break; + case "-pc": + case "-pocore": + case "--prooncorechange": + proOnCoreChange = true; + break; case "-h": case "--help": help = true; @@ -69,28 +80,30 @@ } } +Trace.Listeners.Add(new ConsoleTraceListener()); +Trace.AutoFlush = true; + if (help) { Trace.WriteLine("ESBuild TypeScript -> JavaScript Compilation Script"); Trace.WriteLine(""); Trace.WriteLine("Parameters:"); - Trace.WriteLine(" -f, --force Removes any lock files and forces the script to run"); Trace.WriteLine(" -c, --configuration Build configuration (default is 'Debug')"); Trace.WriteLine(" Valid values are 'Debug' and 'Release'"); Trace.WriteLine(" -p, --pro Run the GeoBlazor Pro ESBuild process"); + Trace.WriteLine(" -pc, --prooncorechange Run the GeoBlazor Pro ESBuild process if pro OR core files have changed"); Trace.WriteLine(" -h, --help Display this help message"); return 0; } -Trace.Listeners.Add(new ConsoleTraceListener()); - Process? dialogProcess = null; if (verbose) { if (dialog) // only start the dialog early if we are in Verbose + Dialog mode { - dialogProcess = StartConsoleDialog(toolsDir, $"GeoBlazor {(pro ? "Pro" : "Core")} ESBuild"); + dialogProcess = StartConsoleDialog(toolsDir, + $"GeoBlazor {(pro ? "Pro" : "Core")} ESBuild", pro); } Trace.WriteLine("Launching ESBuild..."); } @@ -98,10 +111,11 @@ // Normalize configuration configuration = configuration.Equals("release", StringComparison.OrdinalIgnoreCase) ? "Release" : "Debug"; -// The build folder is where this script is located (scriptDir set at top using CallerFilePath) -// Core source is at ../src/dymaptic.GeoBlazor.Core relative to build folder -string coreSourceDir = Path.GetFullPath(Path.Combine(scriptDir, "..", "src", "dymaptic.GeoBlazor.Core")); -string proSourceDir = Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "src", "dymaptic.GeoBlazor.Pro")); +// Scripts are in GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts +// Core source is at GeoBlazor.Pro/GeoBlazor/src/dymaptic.GeoBlazor.Core relative to build folder +// Pro source is at GeoBlazor.Pro/src/dymaptic.GeoBlazor.Pro relative to build folder +string coreSourceDir = Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "src", "dymaptic.GeoBlazor.Core")); +string proSourceDir = Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "..", "src", "dymaptic.GeoBlazor.Pro")); string sourceDir = pro ? proSourceDir : coreSourceDir; string coreScriptsDir = Path.Combine(coreSourceDir, "Scripts"); @@ -116,44 +130,16 @@ string proRecordFilePath = Path.GetFullPath(Path.Combine(proSourceDir, "..", ".esbuild-record.json")); string recordFilePath = pro ? proRecordFilePath : coreRecordFilePath; -string coreDebugLockFile = Path.Combine(coreSourceDir, "esBuild.Debug.lock"); -string coreReleaseLockFile = Path.Combine(coreSourceDir, "esBuild.Release.lock"); -string proDebugLockFile = Path.Combine(proSourceDir, "esBuild.Debug.lock"); -string proReleaseLockFile = Path.Combine(proSourceDir, "esBuild.Release.lock"); -string debugLockFile = configuration == "Debug" ? (pro ? proDebugLockFile : coreDebugLockFile) : string.Empty; -string releaseLockFile = configuration == "Release" ? (pro ? proReleaseLockFile : coreReleaseLockFile) : string.Empty; -string coreLockFilePath = configuration == "Release" ? coreReleaseLockFile : coreDebugLockFile; -string proLockFilePath = configuration == "Release" ? proReleaseLockFile : proDebugLockFile; -string lockFilePath = pro ? proLockFilePath : coreLockFilePath; - -// Handle --force flag: delete record file -if (force && File.Exists(coreRecordFilePath)) -{ - Trace.WriteLine("Force rebuild: Deleting existing record file."); - File.Delete(coreRecordFilePath); -} -if (force && File.Exists(proRecordFilePath)) -{ - Trace.WriteLine("Force rebuild: Deleting existing record file."); - File.Delete(proRecordFilePath); -} - -string currentBranch = GetCurrentGitBranch(coreSourceDir); - +string currentBranch = GetCurrentGitBranch(sourceDir); bool needsBuild = CheckIfNeedsBuild( - coreRecordFilePath, + recordFilePath, currentBranch, - coreScriptsDir, - coreOutputDir); + scriptsDir, outputDir, pro); -if (pro) +if (!needsBuild && pro && proOnCoreChange) { - currentBranch = GetCurrentGitBranch(proSourceDir); - needsBuild = CheckIfNeedsBuild( - proRecordFilePath, - currentBranch, - proScriptsDir, - proOutputDir); + needsBuild = CheckIfNeedsBuild(coreRecordFilePath, + currentBranch, coreScriptsDir, coreOutputDir, false); } if (!needsBuild) @@ -166,37 +152,14 @@ { if (dialog) // start the dialog now if we are not in Verbose mode { - dialogProcess = StartConsoleDialog(toolsDir, $"GeoBlazor {(pro ? "Pro" : "Core")} ESBuild"); + dialogProcess = StartConsoleDialog(toolsDir, + $"GeoBlazor {(pro ? "Pro" : "Core")} ESBuild", pro); } Trace.WriteLine("Launching ESBuild..."); } -// Check if the process is locked for the current configuration -bool locked = configuration == "Debug" && File.Exists(debugLockFile) - || configuration == "Release" && File.Exists(releaseLockFile); - -// Prevent multiple instances of the script from running at the same time -if (locked) -{ - if (force) - { - if (File.Exists(debugLockFile)) File.Delete(debugLockFile); - if (File.Exists(releaseLockFile)) File.Delete(releaseLockFile); - Trace.WriteLine("Cleared esBuild lock files"); - } - else - { - Trace.WriteLine("Another instance of the script is already running. Exiting."); - KillDialog(dialogProcess); - return 1; - } -} - try { - // Lock - File.WriteAllText(lockFilePath, DateTime.UtcNow.ToString("o")); - // Ensure output directory exists if (!Directory.Exists(outputDir)) { @@ -257,17 +220,16 @@ HoldDialog(dialogProcess); return 1; } -finally -{ - // Unlock - if (File.Exists(lockFilePath)) - { - File.Delete(lockFilePath); - } -} -// Helper methods +// ============================================================================ +// Helper Methods +// ============================================================================ +/// +/// Gets the current Git branch name for a repository. +/// +/// The directory within the Git repository. +/// The branch name, or "unknown" if Git is unavailable or fails. static string GetCurrentGitBranch(string workingDirectory) { try @@ -284,19 +246,25 @@ static string GetCurrentGitBranch(string workingDirectory) }; using var process = Process.Start(psi); - if (process is null) return "unknown"; + if (process is null) return "no-git"; string output = process.StandardOutput.ReadToEnd().Trim(); process.WaitForExit(); - return process.ExitCode == 0 ? output : "unknown"; + return process.ExitCode == 0 ? output : "no-git"; } catch { - return "unknown"; + return "no-git"; } } +/// +/// Reads the last build record from the JSON file. +/// The record contains the timestamp of the last successful build and the branch name. +/// +/// Path to the .esbuild-record.json file. +/// A tuple containing the Unix timestamp (milliseconds) and branch name. static (long Timestamp, string Branch) GetLastBuildRecord(string recordFilePath) { if (!File.Exists(recordFilePath)) @@ -321,39 +289,59 @@ static string GetCurrentGitBranch(string workingDirectory) } } -static bool CheckIfNeedsBuild(string recordFilePath, string currentBranch, string scriptsDir, string outputDir) +/// +/// Determines whether a TypeScript build is needed. +/// A build is needed if: the branch changed, scripts were modified since last build, +/// or the output directory is empty. +/// +/// Path to the build record file. +/// The current Git branch name. +/// Path to the TypeScript source files. +/// Path to the JavaScript output directory. +/// True if a build should be performed. +static bool CheckIfNeedsBuild(string recordFilePath, string currentBranch, string scriptsDir, string outputDir, + bool pro) { // Check if build is needed var lastBuild = GetLastBuildRecord(recordFilePath); - bool branchChanged = currentBranch != lastBuild.Branch; + bool branchChanged = currentBranch != "no-git" + && currentBranch != lastBuild.Branch; if (branchChanged) { - Trace.WriteLine($"Git branch changed from \"{lastBuild.Branch}\" to \"{currentBranch}\". Rebuilding..."); + Trace.WriteLine($"{(pro ? "Pro" : "Core")}: Git branch changed from \"{lastBuild.Branch}\" to \"{currentBranch}\". Rebuilding..."); return true; } - if (!GetScriptsModifiedSince(scriptsDir, lastBuild.Timestamp)) + if (!GetScriptsModifiedSince(scriptsDir, lastBuild.Timestamp, pro)) { - Trace.WriteLine("No changes in Scripts folder since last build."); + Trace.WriteLine($"{(pro ? "Pro" : "Core")}: No changes in Scripts folder since last build."); // Check output directory for existing files if (Directory.Exists(outputDir) && Directory.GetFiles(outputDir).Length > 0) { - Trace.WriteLine("Output directory is not empty. Skipping build."); + Trace.WriteLine($"{(pro ? "Pro" : "Core")}: Output directory is not empty. Skipping build."); return false; } else { - Trace.WriteLine("Output directory is empty. Proceeding with build."); + Trace.WriteLine($"{(pro ? "Pro" : "Core")}: Output directory is empty. Proceeding with build."); return true; } } - Trace.WriteLine("Changes detected in Scripts folder. Proceeding with build."); + Trace.WriteLine($"{(pro ? "Pro" : "Core")}: Changes detected in Scripts folder. Proceeding with build."); return true; } +/// +/// Copies TypeScript files from Core's Scripts directory to Pro's Scripts directory. +/// Files listed in Pro's esBuild.js with a "pro_" prefix are renamed accordingly. +/// Only copies files that are newer than the destination. +/// +/// Path to Core's Scripts directory. +/// Path to Pro's Scripts directory. +/// If true, logs each file operation. static void CopyScriptsToPro(string coreScriptsDir, string proScriptsDir, bool verbose) { Trace.WriteLine("Copying core Scripts to Pro Scripts directory..."); @@ -362,6 +350,31 @@ static void CopyScriptsToPro(string coreScriptsDir, string proScriptsDir, bool v Directory.CreateDirectory(proScriptsDir); } + Regex proPrefixedFileRegex = new(@"^\s*'\.\/Scripts\/pro_(?\w+)\.ts',\s*$", + RegexOptions.Compiled); + + string proEsBuildJsFilePath = Path.GetFullPath( + Path.Combine(proScriptsDir, "..", "esBuild.js")); + + List proPrefixedFiles = []; + + foreach (string line in File.ReadAllLines(proEsBuildJsFilePath)) + { + Match match = proPrefixedFileRegex.Match(line); + if (match.Success) + { + string fileName = match.Groups["fileName"].Value; + proPrefixedFiles.Add($"{fileName}.ts"); + Console.WriteLine($"Found pro_ file: {fileName}.ts"); + continue; + } + if (line.TrimStart().StartsWith("chunkNames")) + { + // we are past all the pro_ files + break; + } + } + int copiedCount = 0; int skippedCount = 0; List fileNames = []; @@ -370,7 +383,12 @@ static void CopyScriptsToPro(string coreScriptsDir, string proScriptsDir, bool v { string fileName = Path.GetFileName(filePath); fileNames.Add(fileName); - string destinationPath = Path.Combine(proScriptsDir, fileName); + string destinationFileName = fileName; + if (proPrefixedFiles.Contains(fileName)) + { + destinationFileName = $"pro_{fileName}"; + } + string destinationPath = Path.Combine(proScriptsDir, destinationFileName); if (!File.Exists(destinationPath)) { @@ -404,9 +422,16 @@ static void CopyScriptsToPro(string coreScriptsDir, string proScriptsDir, bool v Trace.WriteLine($"Copied {copiedCount} files, skipped {skippedCount} files."); } +/// +/// Saves the build record to a JSON file after a successful build. +/// Records the current timestamp and branch name for incremental build detection. +/// +/// Path where the record should be saved. +/// The current Git branch name. static void SaveBuildRecord(string recordFilePath, string branch) { - long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + // buffer 30 seconds into the future to avoid edge cases + long timestamp = DateTimeOffset.UtcNow.AddSeconds(30).ToUnixTimeMilliseconds(); // Write JSON manually to avoid reflection-based serialization (not compatible with Native AOT) string json = $$""" { @@ -417,7 +442,14 @@ static void SaveBuildRecord(string recordFilePath, string branch) File.WriteAllText(recordFilePath, json); } -static bool GetScriptsModifiedSince(string scriptsDir, long lastTimestamp) +/// +/// Checks if any TypeScript files in the Scripts directory have been modified +/// since the given timestamp. +/// +/// Path to the Scripts directory to scan. +/// Unix timestamp (milliseconds) of the last build. +/// True if any files have been modified since the timestamp. +static bool GetScriptsModifiedSince(string scriptsDir, long lastTimestamp, bool pro) { if (!Directory.Exists(scriptsDir)) { @@ -426,10 +458,11 @@ static bool GetScriptsModifiedSince(string scriptsDir, long lastTimestamp) var lastBuildTime = DateTimeOffset.FromUnixTimeMilliseconds(lastTimestamp).DateTime; - foreach (string file in Directory.GetFiles(scriptsDir, "*", SearchOption.AllDirectories)) + foreach (string file in Directory.GetFiles(scriptsDir, "*.ts", SearchOption.AllDirectories)) { - if (File.GetLastWriteTime(file) > lastBuildTime) + if (File.GetLastWriteTimeUtc(file) > lastBuildTime) { + Trace.WriteLine($"{(pro ? "Pro" : "Core")}: File {file} modified at {File.GetLastWriteTimeUtc(file).ToLongTimeString()} (last build: {lastBuildTime.ToLongTimeString()})"); return true; } } @@ -437,7 +470,13 @@ static bool GetScriptsModifiedSince(string scriptsDir, long lastTimestamp) return false; } -static Process? StartConsoleDialog(string buildDir, string title) +/// +/// Starts the ConsoleDialog process to display build progress in a separate window. +/// +/// The build-tools directory containing ConsoleDialog.dll. +/// The title for the console window. +/// The started Process, or null if it failed to start. +static Process? StartConsoleDialog(string buildDir, string title, bool pro) { try { @@ -476,7 +515,7 @@ static bool GetScriptsModifiedSince(string scriptsDir, long lastTimestamp) else { dialog.StandardInput.AutoFlush = true; - Trace.Listeners.Add(new DialogTraceListener(dialog)); + Trace.Listeners.Add(new DialogTraceListener(dialog, pro)); } return dialog; @@ -488,6 +527,10 @@ static bool GetScriptsModifiedSince(string scriptsDir, long lastTimestamp) } } +/// +/// Gracefully closes the ConsoleDialog process by sending an "exit" command. +/// +/// The ConsoleDialog process to close. static void KillDialog(Process? dialog) { if (dialog is null || dialog.HasExited) @@ -512,6 +555,14 @@ static void KillDialog(Process? dialog) } } +/// +/// Runs an npm command using PowerShell 7 for cross-platform compatibility. +/// Output is captured and also written to the Trace listeners. +/// +/// The directory to run the command in. +/// The npm command (e.g., "install", "run build"). +/// Optional ConsoleDialog process for output display. +/// A tuple containing the output lines and exit code. static (List Output, int ExitCode) RunNpmCommand(string workingDirectory, string command, Process? dialogProcess) { var output = new List(); @@ -570,6 +621,11 @@ static void KillDialog(Process? dialog) } } +/// +/// Checks if the output contains any error or warning messages. +/// +/// The list of output lines to check. +/// True if any line contains "Error" or "Warning" (case-insensitive). static bool HasErrorOrWarning(List output) { return output.Any(line => @@ -577,31 +633,23 @@ static bool HasErrorOrWarning(List output) line.Contains("Warning", StringComparison.OrdinalIgnoreCase)); } -static string GetScriptsDirectory([CallerFilePath] string? callerFilePath = null) -{ - // When running as a pre-compiled DLL, [CallerFilePath] contains the compile-time path - // which is invalid at runtime (especially in Docker containers). - // Detect this by checking if the file exists at the caller path. - if (!string.IsNullOrEmpty(callerFilePath) && File.Exists(callerFilePath)) - { - return Path.GetDirectoryName(callerFilePath)!; - } - - // Running as a DLL - use AppContext.BaseDirectory which points to the DLL location - // The DLL is in build-tools/, and scripts are in build-scripts/ (sibling directory) - string dllDirectory = AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - return Path.Combine(Path.GetDirectoryName(dllDirectory)!, "build-scripts"); -} - +/// +/// Sends a failure message to the dialog and keeps it open for user review. +/// +/// The ConsoleDialog process. static void HoldDialog(Process? dialog) { if (dialog?.StandardInput is not null && !dialog.HasExited) { - dialog.StandardInput.WriteLine("NPM Install failed"); + dialog.StandardInput.WriteLine("hold"); } } -public class DialogTraceListener(Process dialog) : TraceListener +/// +/// A TraceListener that forwards trace output to a ConsoleDialog process via stdin. +/// This allows build output to be displayed in the popup console window. +/// +public class DialogTraceListener(Process dialog, bool isPro) : TraceListener { public override void Write(string? message) { @@ -612,7 +660,7 @@ public override void Write(string? message) try { - dialog.StandardInput.Write(message); + dialog.StandardInput.Write(isPro ? $"PRO: {message}" : message); } catch { @@ -629,7 +677,7 @@ public override void WriteLine(string? message) try { - dialog.StandardInput.WriteLine(message); + dialog.StandardInput.WriteLine(isPro ? $"PRO: {message}" : message); } catch { diff --git a/build-scripts/ESBuildClearLocks.cs b/build-tools/build-scripts/ESBuildClearLocks.cs similarity index 53% rename from build-scripts/ESBuildClearLocks.cs rename to build-tools/build-scripts/ESBuildClearLocks.cs index 948db0b4b..ea0430c11 100644 --- a/build-scripts/ESBuildClearLocks.cs +++ b/build-tools/build-scripts/ESBuildClearLocks.cs @@ -1,16 +1,38 @@ #!/usr/bin/env dotnet +#:project ../utilities/Utilities.csproj // ESBuild Clear Locks Script // C# file-based app version of esBuildClearLocks.ps1 // Removes ESBuild lock files for both Core and Pro projects // // Usage: dotnet ESBuildClearLocks.cs -// -h, --help Display help message +// -sf --stale-files Remove stale lock files only +// -h, --help Display help message using System.Runtime.CompilerServices; +using Utilities; // Parse command line arguments -bool help = args.Any(a => a is "-h" or "--help"); +bool help = false; +bool staleFilesOnly = false; + +for (int i = 0; i < args.Length; i++) +{ + switch (args[i]) + { + case "-h": + case "--help": + help = true; + break; + case "-sf": + case "--stale-files": + staleFilesOnly = true; + break; + default: + Console.WriteLine($"Unknown argument: {args[i]}"); + return 1; + } +} if (help) { @@ -20,29 +42,34 @@ Console.WriteLine("Usage: dotnet ESBuildClearLocks.cs"); Console.WriteLine(); Console.WriteLine("Options:"); - Console.WriteLine(" -h, --help Display this help message"); + Console.WriteLine(" -h, --help Display this help message"); + Console.WriteLine(" -sf, --stale-files Remove stale lock files only"); return 0; } -string scriptDir = GetScriptDirectory(); +string scriptDir = PathFinder.GetScriptsDirectory(); // Define lock file paths relative to script location (build-scripts folder) string[] lockFiles = [ - Path.GetFullPath(Path.Combine(scriptDir, "..", "src", "dymaptic.GeoBlazor.Core", "esBuild.Debug.lock")), - Path.GetFullPath(Path.Combine(scriptDir, "..", "src", "dymaptic.GeoBlazor.Core", "esBuild.Release.lock")), - Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "src", "dymaptic.GeoBlazor.Pro", "esBuild.Debug.lock")), - Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "src", "dymaptic.GeoBlazor.Pro", "esBuild.Release.lock")) + Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "src", "dymaptic.GeoBlazor.Core", "esBuild.lock")), + Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "..", "src", "dymaptic.GeoBlazor.Pro", "esBuild.lock")) ]; int deletedCount = 0; +DateTime staleFileThreshold = DateTime.UtcNow.AddMinutes(-5); + foreach (string lockFile in lockFiles) { if (File.Exists(lockFile)) { try { + if (staleFilesOnly && File.GetLastWriteTimeUtc(lockFile) > staleFileThreshold) + { + continue; + } File.Delete(lockFile); Console.WriteLine($"Deleted: {lockFile}"); deletedCount++; @@ -58,15 +85,5 @@ { Console.WriteLine($"Cleared {deletedCount} esBuild lock file(s)"); } -else -{ - Console.WriteLine("No esBuild lock files found"); -} return 0; - -// Helper method -static string GetScriptDirectory([CallerFilePath] string? callerFilePath = null) -{ - return Path.GetDirectoryName(callerFilePath!) ?? Environment.CurrentDirectory; -} diff --git a/build-scripts/FetchNuGetVersion.cs b/build-tools/build-scripts/FetchNuGetVersion.cs similarity index 100% rename from build-scripts/FetchNuGetVersion.cs rename to build-tools/build-scripts/FetchNuGetVersion.cs diff --git a/build-tools/build-scripts/GBTest.cs b/build-tools/build-scripts/GBTest.cs new file mode 100644 index 000000000..8153ef121 --- /dev/null +++ b/build-tools/build-scripts/GBTest.cs @@ -0,0 +1,274 @@ +#!/usr/bin/env dotnet + +#:package CliWrap@3.10.0 +#:project ../utilities/Utilities.csproj + +// GBTest.cs - GeoBlazor Test Runner +// Runs the GeoBlazor Core automation test project via `dotnet run`. +// Supports configuration, test filtering, code coverage, and container execution. +// +// Usage: dotnet ./build-scripts/GBTest.cs [options] +// +// Options: +// -h, --help Show this help message and exit +// -c, --configuration Build configuration (default: Release) +// -f, --filter Test filter expression passed to dotnet test +// -p, --percentage Percentage of tests that must pass to be counted as successful (default 100%) +// --cover Enable code coverage collection (sets COVER=true) +// --container Run tests in a container (sets USE_CONTAINER=true) + +using CliWrap; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using Utilities; + +// Default option values +bool cover = false; +bool container = false; +string config = "Release"; +string? filter = null; +int percentage = 100; + +// Parse command-line arguments +for (int i = 0; i < args.Length; i++) +{ + switch (args[i].ToLowerInvariant()) + { + case "-h": + case "--help": + Console.WriteLine(""" + GBTest - GeoBlazor Test Runner + + Usage: dotnet ./build-scripts/GBTest.cs [options] + + Options: + -h, --help Show this help message and exit + -c, --configuration Build configuration (default: Release) + -f, --filter Test filter expression passed to dotnet test + -p, --percentage Percentage of tests that must pass to be counted as successful (default 100%) + --cover Enable code coverage collection (sets COVER=true) + --container Run tests in a container (sets USE_CONTAINER=true) + + Examples: + dotnet ./build-scripts/GBTest.cs + dotnet ./build-scripts/GBTest.cs -c Debug + dotnet ./build-scripts/GBTest.cs --filter "FullyQualifiedName~MapView" + dotnet ./build-scripts/GBTest.cs --cover --container + """); + return 0; + case "--cover": + cover = true; + break; + case "--container": + container = true; + break; + case "-c": + case "--configuration": + if (i + 1 < args.Length) + { + config = args[++i]; + } + break; + case "-f": + case "--filter": + if (i + 1 < args.Length) + { + filter = args[++i]; + } + break; + case "-p": + case "--percentage": + if (i + 1 < args.Length && int.TryParse(args[++i], out int p)) + { + percentage = p; + } + break; + } +} + +CancellationTokenSource cts = new(); +CancellationTokenSource forceCts = new(); + +Console.CancelKeyPress += (_, e) => +{ + e.Cancel = true; // Prevent immediate termination of this script + cts.Cancel(); + forceCts.CancelAfter(20_000); +}; + +// Resolve paths relative to this script's location +string scriptsDir = PathFinder.GetScriptsDirectory(); + +string testProjectDir = Path.GetFullPath( + Path.Combine(scriptsDir, "..", "..", "test", "dymaptic.GeoBlazor.Core.Test.Automation")); + +string testProjectFilePath = Path.Combine(testProjectDir, "dymaptic.GeoBlazor.Core.Test.Automation.csproj"); + +// Build the argument list for `dotnet run` +List buildArgs = +[ + "--project", testProjectFilePath, + "-c", config, + "--ignore-exit-code", "2" +]; + +if (filter != null) +{ + buildArgs = [..buildArgs, "--filter", filter]; +} + +// Set environment variables to toggle optional features +Dictionary? environmentVariables = []; + +if (cover) +{ + environmentVariables["COVER"] = "true"; +} + +if (container) +{ + environmentVariables["USE_CONTAINER"] = "true"; +} + +// Execute the test project +await RunDotnetCommandWithOutputAsync(testProjectDir, "run", buildArgs, environmentVariables, cts.Token, forceCts.Token); + +Console.WriteLine("FINAL SUMMARY"); +Console.WriteLine("-------------------------------------------------------"); + +// Read the test output log +string testOutputLogPath = Path.Combine(testProjectDir, "test-run.log"); + +Regex finalCountRegex = new(@"^.*FINAL_SUMMARY: PASSED TESTS: (?\d+) / (?\d+)\s*$"); + +bool failed = false; +foreach (string line in await File.ReadAllLinesAsync(testOutputLogPath)) +{ + if (line.Contains("FINAL_SUMMARY")) + { + string content = line.Substring(38); // 38 is the timestamp plus FINAL_SUMMARY: + Console.WriteLine(content); + if (finalCountRegex.Match(line) is { Success: true } match) + { + int total = int.Parse(match.Groups["total"].Value); + int passed = int.Parse(match.Groups["passed"].Value); + double passedPercentage = (double)passed / total * 100; + Console.WriteLine($"TEST RESULTS: {passed} / {total} TESTS PASSED ({passedPercentage:F2}%)."); + if (passedPercentage < percentage) + { + Console.WriteLine($"TEST RUN FAILED: Passed percentage {passedPercentage:F2}% is below the required {percentage}%."); + failed = true; + } + } + } +} + +if (failed) +{ + return 1; +} + +return 0; + + +/// +/// Runs a dotnet command and captures both stdout and stderr output. +/// Output is written to the console in real-time. +/// Handles Ctrl+C by forwarding the kill signal to the child process. +/// +/// The working directory for the command. +/// The dotnet command (e.g., "build", "restore"). +/// The arguments to pass to the command. +/// Optional environment variables to set for the process. +/// The exit code of the process. +static async Task RunDotnetCommandWithOutputAsync(string workingDirectory, + string command, IEnumerable args, Dictionary? environmentVariables, + CancellationToken cancellationToken, CancellationToken forceCancellationToken) +{ + bool summaryStarted = false; + bool testLineMatched = false; + bool supportsCursorManipulation = true; + ConsoleColor defaultColor = Console.ForegroundColor; + Regex testLineRegex = new(@"^\[\+(?\d+)\/x(?\d+)\/\?(?\d+)\] (?.*)$"); + + try + { + await Cli.Wrap("dotnet") + .WithArguments($"{command} {string.Join(" ", args.Where(a => !string.IsNullOrWhiteSpace(a)))}") + .WithWorkingDirectory(workingDirectory) + .WithEnvironmentVariables(environmentVariables ?? []) + .WithStandardOutputPipe(PipeTarget.ToDelegate(line => + { + if (!string.IsNullOrWhiteSpace(line) && !summaryStarted) + { + if (line.Contains("Test run summary")) + { + summaryStarted = true; + + return; + } + if (testLineRegex.Match(line) is { Success: true } match && !summaryStarted) + { + if (testLineMatched && supportsCursorManipulation) + { + try + { + // Move cursor up and clear the previous line + int cursorTop = Console.GetCursorPosition().Top; + Console.SetCursorPosition(0, cursorTop - 1); + Console.Write(new string(' ', Console.WindowWidth)); + Console.SetCursorPosition(0, cursorTop - 1); + } + catch (IOException) + { + supportsCursorManipulation = false; + // In some environments (like certain CI systems), the console may not support cursor manipulation. + // If that happens, we just won't clear the previous line and will print updates on new lines instead. + } + } + + int passed = int.Parse(match.Groups["passed"].Value); + int failed = int.Parse(match.Groups["failed"].Value); + int skipped = int.Parse(match.Groups["skipped"].Value); + string content = match.Groups["content"].Value; + Console.ForegroundColor = defaultColor; + Console.Write("["); + Console.ForegroundColor = ConsoleColor.Green; + Console.Write($"√{passed}"); + Console.ForegroundColor = defaultColor; + Console.Write("/"); + Console.ForegroundColor = ConsoleColor.Red; + Console.Write($"x{failed}"); + Console.ForegroundColor = defaultColor; + Console.Write("/"); + Console.ForegroundColor = ConsoleColor.Yellow; + Console.Write($"?{skipped}"); + Console.ForegroundColor = defaultColor; + Console.WriteLine($"] {content}"); + testLineMatched = true; + return; + } + + testLineMatched = false; + Console.WriteLine(line); + } + })) + .WithStandardErrorPipe(PipeTarget.ToDelegate(line => + { + // Suppress macOS malloc stack logging warning that appears on startup + if (!string.IsNullOrWhiteSpace(line) && + !line.Contains("MallocStackLogging: can't turn off malloc stack logging")) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(line); + Console.ForegroundColor = defaultColor; + } + })) + .ExecuteAsync(forceCancellationToken, cancellationToken); + } + catch (OperationCanceledException) + { + Console.WriteLine("Test run was canceled."); + } +} \ No newline at end of file diff --git a/build-scripts/GeoBlazorBuild.cs b/build-tools/build-scripts/GeoBlazorBuild.cs similarity index 69% rename from build-scripts/GeoBlazorBuild.cs rename to build-tools/build-scripts/GeoBlazorBuild.cs index 8ed261266..18151684f 100644 --- a/build-scripts/GeoBlazorBuild.cs +++ b/build-tools/build-scripts/GeoBlazorBuild.cs @@ -1,5 +1,8 @@ #!/usr/bin/env dotnet +#:package Polly.Core@8.6.5 +#:project ../utilities/Utilities.csproj + // GeoBlazorBuild - Primary build script for GeoBlazor and GeoBlazor Pro // Usage: dotnet GeoBlazorBuild.dll [options] or ./GeoBlazorBuild.exe [options] // -pro Build GeoBlazor Pro as well as Core (default is false) @@ -17,17 +20,18 @@ // -h, --help Display this help message using System.Diagnostics; -using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.RegularExpressions; using System.Xml.Linq; +using Polly; +using Utilities; + // Paths // Get the script cs file path, that way we can run this script from either the CS file or the Executable -string scriptsDir = GetScriptsDirectory(); -string toolsDir = Path.GetFullPath(Path.Combine(scriptsDir, "..", "build-tools")); -// Build folder is at GeoBlazor/build/, Core root is GeoBlazor/ -string coreRepoRoot = Path.GetFullPath(Path.Combine(toolsDir, "..")); +string scriptsDir = PathFinder.GetScriptsDirectory(); +// Scripts folder is at GeoBlazor/build-tools/build-scripts/, Core root is GeoBlazor/ +string coreRepoRoot = Path.GetFullPath(Path.Combine(scriptsDir, "..", "..")); string proRepoRoot = Path.GetFullPath(Path.Combine(coreRepoRoot, "..")); string corePropsPath = Path.Combine(coreRepoRoot, "Directory.Build.props"); @@ -159,6 +163,17 @@ return 0; } +CancellationTokenSource cts = new CancellationTokenSource(); + +// Handle Ctrl+C by killing the child process +Console.CancelKeyPress += async (_, e) => +{ + e.Cancel = true; // Prevent immediate termination of this script + Console.WriteLine("Cancellation requested, waiting for current operations to complete..."); + await cts.CancelAsync(); + Environment.Exit(1); +}; + // If generating docs, also generate XML comments if (generateDocs) { @@ -177,58 +192,18 @@ Console.WriteLine($"Validator Configuration: {validatorConfig}"); Console.WriteLine($"License Server URL: {serverUrl}"); -string binlogFlag = binlog ? "-bl" : ""; - int step = 1; DateTime stepStartTime = DateTime.Now; // Step 1: ensure other build scripts are up to date: WriteStepHeader(step, "Updating build scripts to latest versions..."); -Console.WriteLine($"Running {scriptsDir}/ScriptBuilder.cs to update build scripts in {toolsDir}"); -await RunDotnetCommandWithOutputAsync(scriptsDir, "run", "ScriptBuilder.cs", "--exclude", "GeoBlazorBuild.cs"); +Console.WriteLine($"Running {scriptsDir}/ScriptBuilder.cs to update build scripts"); +await RunDotnetCommand(scriptsDir, "run", null, cts.Token, "ScriptBuilder.cs", "--exclude", "GeoBlazorBuild.cs"); WriteStepCompleted(step, stepStartTime); step++; -string otherConfiguration = configuration.Equals("Release", StringComparison.OrdinalIgnoreCase) ? "Debug" : "Release"; - -// Lock file paths -string coreLockFilePath = Path.Combine(coreProjectPath, $"esBuild.{otherConfiguration}.lock"); -string proLockFilePath = Path.Combine(proProjectPath, $"esProBuild.{otherConfiguration}.lock"); - -// Check for existing locks from other configuration -if (File.Exists(coreLockFilePath) || File.Exists(proLockFilePath)) -{ - Console.WriteLine("Another instance of the esBuild scripts are already running, please wait."); - while (File.Exists(coreLockFilePath) || File.Exists(proLockFilePath)) - { - Thread.Sleep(1000); - Console.Write("."); - if (scriptStartTime.AddMinutes(1) < DateTime.Now) - { - if (File.Exists(coreLockFilePath)) - { - Console.WriteLine($"\nLock file {coreLockFilePath} still exists after 1 minute, removing stale lock."); - File.Delete(coreLockFilePath); - } - if (File.Exists(proLockFilePath)) - { - Console.WriteLine($"\nLock file {proLockFilePath} still exists after 1 minute, removing stale lock."); - File.Delete(proLockFilePath); - } - } - } - Console.WriteLine("Lock released, continuing..."); -} - -// Create lock files for current configuration -string currentCoreLockFilePath = Path.Combine(coreProjectPath, $"esBuild.{configuration}.lock"); -string currentProLockFilePath = Path.Combine(proProjectPath, $"esProBuild.{configuration}.lock"); - try { - // Set environment variable - Environment.SetEnvironmentVariable("PipelineBuild", "true"); - string version = customVersion ?? ""; bool customVersionSet = !string.IsNullOrEmpty(customVersion); @@ -236,8 +211,8 @@ stepStartTime = DateTime.Now; WriteStepHeader(step, "Cleaning old build artifacts"); - await RunDotnetCommand(coreProjectPath, "clean", - $"\"{Path.Combine(coreProjectPath, "dymaptic.GeoBlazor.Core.csproj")}\"", "/p:PipelineBuild=true"); + await RunDotnetCommand(coreProjectPath, "clean", null, cts.Token, + $"\"{Path.Combine(coreProjectPath, "dymaptic.GeoBlazor.Core.csproj")}\""); DeleteDirectoryIfExists(Path.Combine(coreProjectPath, "bin")); DeleteDirectoryIfExists(Path.Combine(coreProjectPath, "obj")); DeleteDirectoryContentsIfExists(Path.Combine(coreProjectPath, "wwwroot", "js")); @@ -245,8 +220,8 @@ await RunDotnetCommand(coreProjectPath, "clean", if (pro) { - await RunDotnetCommand(proProjectPath, "clean", - $"\"{Path.Combine(proProjectPath, "dymaptic.GeoBlazor.Pro.csproj")}\"", "/p:PipelineBuild=true"); + await RunDotnetCommand(proProjectPath, "clean", null, cts.Token, + $"\"{Path.Combine(proProjectPath, "dymaptic.GeoBlazor.Pro.csproj")}\""); DeleteDirectoryIfExists(Path.Combine(proProjectPath, "bin")); DeleteDirectoryIfExists(Path.Combine(proProjectPath, "obj")); DeleteDirectoryContentsIfExists(Path.Combine(proProjectPath, "obf")); @@ -256,7 +231,7 @@ await RunDotnetCommand(proProjectPath, "clean", if (Directory.Exists(validatorProjectPath)) { - await RunDotnetCommand(validatorProjectPath, "clean", + await RunDotnetCommand(validatorProjectPath, "clean", null, cts.Token, $"\"{Path.Combine(validatorProjectPath, "dymaptic.GeoBlazor.Pro.V.csproj")}\""); DeleteDirectoryIfExists(Path.Combine(validatorProjectPath, "bin")); DeleteDirectoryIfExists(Path.Combine(validatorProjectPath, "obj")); @@ -326,57 +301,11 @@ await RunDotnetCommand(validatorProjectPath, "clean", step++; } - // Check for another lock for the current configuration - if (File.Exists(currentCoreLockFilePath) && - File.GetLastWriteTimeUtc(currentCoreLockFilePath) < DateTime.UtcNow.AddSeconds(-5)) - { - Console.WriteLine("Another instance of the esBuild script is already running, please wait."); - while (File.Exists(currentCoreLockFilePath)) - { - Thread.Sleep(1000); - Console.Write("."); - } - Console.WriteLine("Lock released, continuing..."); - } - - // Step 4: Build Core JavaScript - stepStartTime = DateTime.Now; - WriteStepHeader(step, "Building Core JavaScript"); - - int esBuildResult = await RunDotnetScriptAsync(toolsDir, "ESBuild.dll", $"-c {configuration}"); - if (esBuildResult != 0) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine($"ERROR: ESBuild.dll failed with exit code {esBuildResult}. Exiting."); - Console.ResetColor(); - return 1; - } - - // Verify JavaScript files were created - string coreJsPath = Path.Combine(coreProjectPath, "wwwroot", "js"); - if (!Directory.Exists(coreJsPath) || Directory.GetFiles(coreJsPath, "*.js").Length == 0) - { - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine($"WARNING: Core JavaScript files not found at {coreJsPath}, waiting..."); - Console.ResetColor(); - Thread.Sleep(2000); - if (!Directory.Exists(coreJsPath) || Directory.GetFiles(coreJsPath, "*.js").Length == 0) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("ERROR: Core JavaScript files still not found after waiting. Exiting."); - Console.ResetColor(); - return 1; - } - } - - WriteStepCompleted(step, stepStartTime); - step++; - // Step 5: Restore .NET packages for Core stepStartTime = DateTime.Now; WriteStepHeader(step, "Restoring .NET Packages"); - await RunDotnetCommand(coreProjectPath, "restore", "/p:PipelineBuild=true"); + await RunDotnetCommand(coreProjectPath, "restore", null, cts.Token); WriteStepCompleted(step, stepStartTime); step++; @@ -385,61 +314,35 @@ await RunDotnetCommand(validatorProjectPath, "clean", stepStartTime = DateTime.Now; WriteStepHeader(step, "Building Core Project and NuGet Package"); - string[] coreBuildArgs = + List coreBuildArgs = [ - $"dymaptic.GeoBlazor.Core.csproj", + $"dymaptic.GeoBlazor.Core.csproj", $"--no-restore", "-c", configuration, - $"/p:PipelineBuild=true", $"/p:GenerateDocs={generateDocs.ToString().ToLower()}", $"/p:GenerateXmlComments={generateXmlComments.ToString().ToLower()}", $"/p:CoreVersion={version}", $"/p:GeneratePackage={package.ToString().ToLower()}", - binlogFlag + "/p:ShowScriptDialogs=false" ]; - Console.WriteLine($"Executing 'dotnet build {string.Join(" ", coreBuildArgs)}'"); - - bool coreHasError = false; - for (int i = 1; i <= buildRetries; i++) + if (binlog) { - try - { - (int exitCode, List output) = await RunDotnetCommandWithOutputAsync(coreProjectPath, "build", coreBuildArgs); + coreBuildArgs.Add($"-bl:\"{Path.Combine(coreRepoRoot, $"core_build_{configuration.ToLower()}.binlog")}\""); + } - if (exitCode != 0) - { - Console.WriteLine($"ERROR: Core Build command failed with exit code {exitCode}. Exiting."); - coreHasError = true; - } - else - { - coreHasError = output.Any(line => - Regex.IsMatch(line, @"[1-9][0-9]* [Ee]rror(s)?") || - line.Contains("Build FAILED")); - if (!coreHasError) - { - break; - } - } - } - catch (Exception ex) - { - coreHasError = true; - Console.WriteLine($"Build attempt {i} of {buildRetries} failed with exception: {ex.Message}"); - } + Console.WriteLine($"Executing 'dotnet build {string.Join(" ", coreBuildArgs)}'"); - Console.WriteLine($"Build attempt {i} of {buildRetries} failed."); - if (i < buildRetries) - { - Console.WriteLine("Waiting 2 seconds before retrying..."); - Thread.Sleep(2000); - } - } + await RunDotnetCommand(coreProjectPath, "build", null, cts.Token, coreBuildArgs); - if (coreHasError) + // Verify JavaScript files were created + string coreJsPath = Path.Combine(coreProjectPath, "wwwroot", "js"); + if (!Directory.Exists(coreJsPath) || Directory.GetFiles(coreJsPath, "*.js").Length == 0) { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("ERROR: Core JavaScript files still not found after waiting. Exiting."); + Console.ResetColor(); return 1; } @@ -471,7 +374,7 @@ await RunDotnetCommand(validatorProjectPath, "clean", stepStartTime = DateTime.Now; WriteStepHeader(step, "Restoring .NET Packages"); - await RunDotnetCommand(proProjectPath, "restore", "/p:PipelineBuild=true"); + await RunDotnetCommand(proProjectPath, "restore", null, cts.Token); WriteStepCompleted(step, stepStartTime); step++; @@ -524,153 +427,90 @@ await RunDotnetCommand(validatorProjectPath, "clean", } // Build validator - string[] validatorBuildArgs = + List validatorBuildArgs = [ "dymaptic.GeoBlazor.Pro.V.csproj", $"/p:OptOutFromObfuscation={optOutFromObfuscation.ToString().ToLower()}", $"/p:ProVersion={version}", "-c", validatorConfig, - binlogFlag + "/p:ShowScriptDialogs=false" ]; - - (int validatorExitCode, List validatorOutput) = await RunDotnetCommandWithOutputAsync(validatorProjectPath, "build", validatorBuildArgs); - - // Restore the ServerUrls in the Validator project - devValidatorContent = File.ReadAllText(devBuildValidatorPath); - devValidatorContent = Regex.Replace( - devValidatorContent, - @"public string SU \{ get; set; \} = "".*"";", - "public string SU { get; set; } = null!;"); - File.WriteAllText(devBuildValidatorPath, devValidatorContent); - - publishValidatorContent = File.ReadAllText(publishTaskValidatorPath); - publishValidatorContent = Regex.Replace( - publishValidatorContent, - @"public string SU \{ get; set; \} = "".*"";", - "public string SU { get; set; } = null!;"); - File.WriteAllText(publishTaskValidatorPath, publishValidatorContent); - - bool validatorHasError = validatorOutput.Any(line => - Regex.IsMatch(line, @"[1-9][0-9]* [Ee]rror(s)?") || - line.Contains("Build FAILED")); - if (validatorHasError) + + if (binlog) { - return 1; + validatorBuildArgs.Add($"-bl:\"{Path.Combine(coreRepoRoot, $"validator_build_{validatorConfig.ToLower()}.binlog")}\""); } - WriteStepCompleted(step, stepStartTime); - step++; - } - - // Check for Pro lock - if (File.Exists(currentProLockFilePath) && - File.GetLastWriteTimeUtc(currentProLockFilePath) < DateTime.UtcNow.AddSeconds(-5)) - { - Console.WriteLine("Another instance of the esBuild scripts are already running, please wait."); - while (File.Exists(currentProLockFilePath)) + try { - Thread.Sleep(1000); - Console.Write("."); + await RunDotnetCommand(validatorProjectPath, "build", null, cts.Token, validatorBuildArgs); } - Console.WriteLine("Lock released, continuing..."); - } - - // Step 9: Build Pro JavaScript - stepStartTime = DateTime.Now; - WriteStepHeader(step, "Building Pro JavaScript"); - - int esProBuildResult = await RunDotnetScriptAsync(toolsDir, "ESBuild.dll", $"-c {configuration} --pro"); - if (esProBuildResult != 0) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine($"ERROR: esProBuild failed with exit code {esProBuildResult}. Exiting."); - Console.ResetColor(); - return 1; - } - - // Verify Pro JavaScript files were created - string proJsPath = Path.Combine(proProjectPath, "wwwroot", "js"); - if (!Directory.Exists(proJsPath) || Directory.GetFiles(proJsPath, "*.js").Length == 0) - { - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine($"WARNING: Pro JavaScript files not found at {proJsPath}, waiting..."); - Console.ResetColor(); - Thread.Sleep(2000); - if (!Directory.Exists(proJsPath) || Directory.GetFiles(proJsPath, "*.js").Length == 0) + finally { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("ERROR: Pro JavaScript files still not found after waiting. Exiting."); - Console.ResetColor(); - return 1; + // Restore the ServerUrls in the Validator project even if the build fails + devValidatorContent = File.ReadAllText(devBuildValidatorPath); + devValidatorContent = Regex.Replace( + devValidatorContent, + @"public string SU \{ get; set; \} = "".*"";", + "public string SU { get; set; } = null!;"); + File.WriteAllText(devBuildValidatorPath, devValidatorContent); + + publishValidatorContent = File.ReadAllText(publishTaskValidatorPath); + publishValidatorContent = Regex.Replace( + publishValidatorContent, + @"public string SU \{ get; set; \} = "".*"";", + "public string SU { get; set; } = null!;"); + File.WriteAllText(publishTaskValidatorPath, publishValidatorContent); } - } - WriteStepCompleted(step, stepStartTime); - step++; + WriteStepCompleted(step, stepStartTime); + step++; + } // Step 10: Build Pro project and package stepStartTime = DateTime.Now; WriteStepHeader(step, "Building Pro project and package"); - string[] proBuildArgs = + List proBuildArgs = [ $"dymaptic.GeoBlazor.Pro.csproj", "--no-restore", $"-c", configuration, - $"/p:PipelineBuild=true", $"/p:GenerateDocs={generateDocs.ToString().ToLower()}", $"/p:GenerateXmlComments={generateXmlComments.ToString().ToLower()}", $"/p:CoreVersion={version}", $"/p:ProVersion={version}", $"/p:OptOutFromObfuscation={optOutFromObfuscation.ToString().ToLower()}", $"/p:GeneratePackage={package.ToString().ToLower()}", - binlogFlag + "/p:ShowScriptDialogs=false" ]; - Console.WriteLine($"Executing 'dotnet {string.Join(" ", proBuildArgs)}'"); - - bool proHasError = false; - for (int i = 1; i <= buildRetries; i++) + if (binlog) { - try - { - (int exitCode, List output) = await RunDotnetCommandWithOutputAsync(proProjectPath, "build", proBuildArgs); + proBuildArgs.Add($"-bl:\"{Path.Combine(coreRepoRoot, $"pro_build_{configuration.ToLower()}.binlog")}\""); + } - if (exitCode != 0) - { - Console.WriteLine($"ERROR: Pro Build command failed with exit code {exitCode}. Exiting."); - proHasError = true; - } - else - { - proHasError = output.Any(line => - Regex.IsMatch(line, @"[1-9][0-9]* [Ee]rror(s)?") || - line.Contains("Build FAILED")); - if (!proHasError) - { - break; - } - } - } - catch (Exception ex) - { - proHasError = true; - Console.WriteLine($"Build attempt {i} of {buildRetries} failed with exception: {ex.Message}"); - } + Console.WriteLine($"Executing 'dotnet {string.Join(" ", proBuildArgs)}'"); - Console.WriteLine($"Build attempt {i} of {buildRetries} failed."); - if (i < buildRetries) - { - Console.WriteLine("Waiting 2 seconds before retrying..."); - Thread.Sleep(2000); - } - } + await RunDotnetCommand(proProjectPath, "build", null, cts.Token, proBuildArgs); - if (proHasError) + // Verify Pro JavaScript files were created + string proJsPath = Path.Combine(proProjectPath, "wwwroot", "js"); + if (!Directory.Exists(proJsPath) || Directory.GetFiles(proJsPath, "*.js").Length == 0) { - return 1; + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine($"WARNING: Pro JavaScript files not found at {proJsPath}, waiting..."); + Console.ResetColor(); + Thread.Sleep(2000); + if (!Directory.Exists(proJsPath) || Directory.GetFiles(proJsPath, "*.js").Length == 0) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("ERROR: Pro JavaScript files still not found after waiting. Exiting."); + Console.ResetColor(); + return 1; + } } if (package) @@ -705,16 +545,6 @@ await RunDotnetCommand(validatorProjectPath, "clean", { TimeSpan totalTime = DateTime.Now - scriptStartTime; - // Remove lock files - if (File.Exists(currentCoreLockFilePath)) - { - File.Delete(currentCoreLockFilePath); - } - if (File.Exists(currentProLockFilePath)) - { - File.Delete(currentProLockFilePath); - } - Console.WriteLine(); Console.BackgroundColor = ConsoleColor.DarkBlue; Console.ForegroundColor = ConsoleColor.White; @@ -723,24 +553,15 @@ await RunDotnetCommand(validatorProjectPath, "clean", Console.WriteLine(); } -// Helper methods - -static string GetScriptsDirectory([CallerFilePath] string? callerFilePath = null) -{ - // When running as a pre-compiled DLL, [CallerFilePath] contains the compile-time path - // which is invalid at runtime (especially in Docker containers). - // Detect this by checking if the file exists at the caller path. - if (!string.IsNullOrEmpty(callerFilePath) && File.Exists(callerFilePath)) - { - return Path.GetDirectoryName(callerFilePath)!; - } - - // Running as a DLL - use AppContext.BaseDirectory which points to the DLL location - // The DLL is in build-tools/, and scripts are in build-scripts/ (sibling directory) - string dllDirectory = AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - return Path.Combine(Path.GetDirectoryName(dllDirectory)!, "build-scripts"); -} +// ============================================================================ +// Helper Methods +// ============================================================================ +/// +/// Writes a formatted step header to the console with colored background. +/// +/// The step number. +/// A description of what this step does. static void WriteStepHeader(int step, string description) { Console.WriteLine(); @@ -752,6 +573,11 @@ static void WriteStepHeader(int step, string description) Console.WriteLine(); } +/// +/// Writes a step completion message showing elapsed time. +/// +/// The step number that completed. +/// The time when this step started. static void WriteStepCompleted(int step, DateTime stepStartTime) { TimeSpan elapsed = DateTime.Now - stepStartTime; @@ -762,6 +588,14 @@ static void WriteStepCompleted(int step, DateTime stepStartTime) Console.WriteLine(); } +/// +/// Deletes a directory and all its contents if it exists. +/// +/// The directory path to delete. +/// +/// If true, uses PowerShell 7 for deletion which handles long paths on Windows. +/// Useful for node_modules directories. +/// static void DeleteDirectoryIfExists(string path, bool usePowerShell = false) { if (!Directory.Exists(path)) @@ -817,6 +651,10 @@ static void DeleteDirectoryIfExists(string path, bool usePowerShell = false) } } +/// +/// Deletes all files within a directory recursively, but keeps the directory structure. +/// +/// The directory whose contents should be deleted. static void DeleteDirectoryContentsIfExists(string path) { if (Directory.Exists(path)) @@ -835,113 +673,96 @@ static void DeleteDirectoryContentsIfExists(string path) } } -static async Task RunDotnetCommand(string workingDirectory, string command, params string[] args) +static async Task LaunchResilientTask(string taskName, Func task, + CancellationToken cancellationToken) { - var psi = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = $"{command} {string.Join(" ", args.Where(a => !string.IsNullOrWhiteSpace(a)))}", - WorkingDirectory = workingDirectory, - UseShellExecute = false, - CreateNoWindow = true - }; + var context = ResilienceContextPool.Shared.Get( + new ResilienceContextCreationArguments(taskName, null, cancellationToken)); + await ResilienceSetup.AppRetryPipeline.ExecuteAsync(task, context); - using var process = Process.Start(psi); - if (process != null) - { - await process.WaitForExitAsync(); - } + ResilienceContextPool.Shared.Return(context); } -static async Task<(int ExitCode, List Output)> RunDotnetCommandWithOutputAsync(string workingDirectory, - string command, params string[] args) +/// +/// Runs a dotnet command without capturing output. +/// +/// The working directory for the command. +/// The dotnet command (e.g., "build", "restore", "clean"). +/// Additional arguments to pass to the command. +static async Task RunDotnetCommand(string workingDirectory, string command, Dictionary? environmentVariables, + CancellationToken cancellationToken, params IEnumerable args) { - var output = new List(); - + string arguments = $"{command} {string.Join(" ", args.Where(a => !string.IsNullOrWhiteSpace(a)))}"; var psi = new ProcessStartInfo { FileName = "dotnet", - Arguments = $"{command} {string.Join(" ", args.Where(a => !string.IsNullOrWhiteSpace(a)))}", + Arguments = arguments, WorkingDirectory = workingDirectory, - RedirectStandardOutput = true, - RedirectStandardError = true, UseShellExecute = false, - CreateNoWindow = true - }; - - using var process = Process.Start(psi); - if (process == null) - { - return (1, ["Failed to start dotnet"]); - } - - process.OutputDataReceived += (_, e) => - { - if (e.Data != null) - { - Console.WriteLine(e.Data); - output.Add(e.Data); - } + CreateNoWindow = true, + RedirectStandardError = true, + RedirectStandardOutput = true }; - process.ErrorDataReceived += (_, e) => + if (environmentVariables != null) { - if (e.Data != null) + foreach (var kvp in environmentVariables) { - Console.WriteLine(e.Data); - output.Add(e.Data); + psi.Environment[kvp.Key] = kvp.Value; } - }; - - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - await process.WaitForExitAsync(); - - return (process.ExitCode, output); -} - -static async Task RunDotnetScriptAsync(string workingDirectory, string scriptName, string args) -{ - var psi = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = $"{scriptName} {args}", - WorkingDirectory = workingDirectory, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true - }; - - using var process = Process.Start(psi); - if (process == null) - { - return 1; } - process.OutputDataReceived += (_, e) => + await LaunchResilientTask($"dotnet {arguments}", async (context) => { - if (e.Data != null) - { - Console.WriteLine(e.Data); - } - }; - process.ErrorDataReceived += (_, e) => - { - if (e.Data != null) + using var process = Process.Start(psi); + if (process != null) { - Console.WriteLine(e.Data); - } - }; + process.OutputDataReceived += (_, e) => + { + if (e.Data != null) + { + Console.WriteLine(e.Data); + if (e.Data.Contains("Build FAILED", StringComparison.OrdinalIgnoreCase)) + { + throw new Exception($"Build failed for process {psi.FileName} {psi.Arguments}."); + } + } + }; - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - await process.WaitForExitAsync(); + process.ErrorDataReceived += (_, e) => + { + if (e.Data != null) + { + Console.WriteLine(e.Data); + if (e.Data.Contains("Build FAILED", StringComparison.OrdinalIgnoreCase)) + { + throw new Exception($"Build failed for process {psi.FileName} {psi.Arguments}."); + } + } + }; - return process.ExitCode; + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + await process.WaitForExitAsync(cancellationToken); + if (!process.HasExited) + { + process.Kill(true); + } + } + }, cancellationToken); } +/// +/// Increments the version number in Directory.Build.props. +/// For publish builds, checks NuGet for the latest version and increments appropriately. +/// For non-publish builds, simply increments the build number. +/// +/// The repository root containing Directory.Build.props. +/// If true, prepares a release version (3-digit, checks NuGet). +/// If true, updates ProVersion; otherwise updates CoreVersion. +/// The new version string. static async Task BumpVersionAsync(string repoRoot, bool publish, bool isPro) { string directoryBuildPropsPath = Path.Combine(repoRoot, "Directory.Build.props"); @@ -1044,6 +865,16 @@ static async Task BumpVersionAsync(string repoRoot, bool publish, bool i return newVersion; } +/// +/// Compares two semantic version strings. +/// +/// The first version string (e.g., "4.33.1.5"). +/// The second version string. +/// +/// Less than 0 if version1 is less than version2; +/// 0 if they are equal; +/// Greater than 0 if version1 is greater than version2. +/// static int CompareVersions(string version1, string version2) { // Simple version comparison - extract numeric parts @@ -1072,3 +903,25 @@ static int CompareVersions(string version1, string version2) if (patch1 != patch2) return patch1.CompareTo(patch2); return build1.CompareTo(build2); } + +static class ResilienceSetup +{ + public static ResiliencePipeline AppRetryPipeline = new ResiliencePipelineBuilder() + .AddRetry(new() + { + BackoffType = DelayBackoffType.Exponential, + MaxRetryAttempts = 3, + Delay = TimeSpan.FromSeconds(1), + OnRetry = context => + { + Console.WriteLine($"Attempt #{context.AttemptNumber + 1} for task failed. Retrying...", + context.Context.OperationKey); + context.Context.Properties.Set(retryAttemptKey, context.AttemptNumber); + + return ValueTask.CompletedTask; + } + }) + .Build(); + + private static ResiliencePropertyKey retryAttemptKey = new("RetryAttempt"); +} \ No newline at end of file diff --git a/build-tools/build-scripts/RazorCopy.cs b/build-tools/build-scripts/RazorCopy.cs new file mode 100644 index 000000000..b719ffd97 --- /dev/null +++ b/build-tools/build-scripts/RazorCopy.cs @@ -0,0 +1,139 @@ +#!/usr/bin/env dotnet +#:project ../utilities/Utilities.csproj + +// RazorCopy Script +// Copies .razor sample files from Core and Pro to the docs/assets/samples folder as .txt files +// +// Usage: dotnet RazorCopy.cs +// -h, --help Display help message +// -v, --verbose Show detailed output + +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using Utilities; + +// Parse command line arguments +bool help = args.Any(a => a is "-h" or "--help"); +bool verbose = args.Any(a => a is "-v" or "--verbose"); + +if (help) +{ + Console.WriteLine("RazorCopy Script"); + Console.WriteLine("Copies .razor sample files from Core and Pro to the docs/assets/samples folder as .txt files."); + Console.WriteLine("Excludes files starting with 'CustomerTests'."); + Console.WriteLine(); + Console.WriteLine("Usage: dotnet RazorCopy.cs"); + Console.WriteLine(); + Console.WriteLine("Options:"); + Console.WriteLine(" -h, --help Display this help message"); + Console.WriteLine(" -v, --verbose Show detailed output"); + return 0; +} + +string scriptDir = PathFinder.GetScriptsDirectory(); + +// Define paths relative to script location (build-scripts folder) +// Script is in: GeoBlazor.Pro/GeoBlazor/build-scripts/ +// Core pages: GeoBlazor.Pro/GeoBlazor/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/ +// Pro pages: GeoBlazor.Pro/samples/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/ +// Save path: GeoBlazor.Pro/docs/assets/samples/ + +string corePagesPath = Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "samples", "dymaptic.GeoBlazor.Core.Sample.Shared", "Pages")); +string proPagesPath = Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "..", "samples", "dymaptic.GeoBlazor.Pro.Sample.Shared", "Pages")); +string savePath = Path.GetFullPath(Path.Combine(scriptDir, "..", "..", "..", "docs", "assets", "samples")); + +if (verbose) +{ + Console.WriteLine($"Core pages path: {corePagesPath}"); + Console.WriteLine($"Pro pages path: {proPagesPath}"); + Console.WriteLine($"Save path: {savePath}"); + Console.WriteLine(); +} + +// Ensure save directory exists +if (!Directory.Exists(savePath)) +{ + Directory.CreateDirectory(savePath); + if (verbose) + { + Console.WriteLine($"Created directory: {savePath}"); + } +} +else +{ + // Clear existing .txt files + var existingFiles = Directory.GetFiles(savePath, "*.txt"); + foreach (string file in existingFiles) + { + File.Delete(file); + if (verbose) + { + Console.WriteLine($"Deleted existing file: {Path.GetFileName(file)}"); + } + } +} + +int copiedCount = 0; + +// Copy Pro pages +if (Directory.Exists(proPagesPath)) +{ + copiedCount += CopyRazorFiles(proPagesPath, savePath, "Pro", verbose); +} +else +{ + Console.WriteLine($"Warning: Pro pages directory not found: {proPagesPath}"); +} + +// Copy Core pages +if (Directory.Exists(corePagesPath)) +{ + copiedCount += CopyRazorFiles(corePagesPath, savePath, "Core", verbose); +} +else +{ + Console.WriteLine($"Warning: Core pages directory not found: {corePagesPath}"); +} + +Console.WriteLine($"Copied {copiedCount} razor file(s) to {savePath}"); +return 0; + +// Helper methods +static int CopyRazorFiles(string sourcePath, string destPath, string label, bool verbose) +{ + int count = 0; + string[] excludePatterns = + [ + "CustomerTests.razor.*", + "^Home.razor.*", + "^SourceCode.razor.*", + "^Test.razor.*", + "^Tests.razor.*", + @"^[A-Za-z\.]+\.css$" + ]; + var razorFiles = Directory.GetFiles(sourcePath, "*.razor*") + .Where(f => !excludePatterns.Any(p => Regex.Match(Path.GetFileName(f), p).Success)) + .ToArray(); + + foreach (string file in razorFiles) + { + string fileName = Path.GetFileName(file); + string destFile = Path.Combine(destPath, fileName + ".txt"); + + try + { + File.Copy(file, destFile, overwrite: true); + count++; + if (verbose) + { + Console.WriteLine($"[{label}] Copied: {fileName}"); + } + } + catch (Exception ex) + { + Console.WriteLine($"Failed to copy {fileName}: {ex.Message}"); + } + } + + return count; +} \ No newline at end of file diff --git a/build-tools/build-scripts/ScriptBuilder.cs b/build-tools/build-scripts/ScriptBuilder.cs new file mode 100644 index 000000000..e29a863ac --- /dev/null +++ b/build-tools/build-scripts/ScriptBuilder.cs @@ -0,0 +1,426 @@ +#!/usr/bin/env dotnet + +// Script Builder - Compiles C# build scripts to DLLs +// ==================================================== +// Builds all C# file-based apps in the build-scripts directory using 'dotnet build'. +// Outputs compiled DLLs to the ../build-tools/ directory for faster execution. +// +// This tool is used to pre-compile the build scripts so they can be run as DLLs +// rather than interpreted C# files, significantly improving startup time. +// +// Usage: +// dotnet ScriptBuilder.cs Build all scripts +// dotnet ScriptBuilder.cs Script1.cs Script2.cs Build only specified scripts +// dotnet ScriptBuilder.cs --exclude Script1.cs Build all except specified scripts +// +// Options: +// --exclude When specified, the listed scripts will be skipped instead of included +// --force, -f Force rebuild of all scripts regardless of changes +// --linux, -l Build for Linux platform +// --mac, -m Build for macOS platform +// --win, -w Build for Windows platform +// --allPlatforms Build for all platforms +// --help, -h Show this help message +// +// Output: +// Compiled DLLs are placed in GeoBlazor/build-tools/ directory +// +// Note: ScriptBuilder.cs itself is always skipped to avoid self-compilation issues. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text.Json; + +bool excludeMode = false; +HashSet scriptsToProcess = new(); +string scriptsDir = GetScriptsDirectory(); +string coreDir = Path.GetFullPath(Path.Combine(scriptsDir, "..", "..")); + +Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); +Trace.WriteLine("Starting ScriptBuilder..."); + +string[] scripts = Directory.GetFiles(scriptsDir, "*.cs"); +bool force = false; +bool allPlatforms = false; +string os = OperatingSystem.IsWindows() + ? "win" + : OperatingSystem.IsMacOS() + ? "osx" + : "linux"; +string arch = RuntimeInformation.OSArchitecture.ToString().ToLowerInvariant(); +string runtime = $"{os}-{arch}"; + +for (int i = 0; i < args.Length; i++) +{ + string arg = args[i]; + + switch (arg.ToLowerInvariant()) + { + case "--exclude": + excludeMode = true; + break; + case "--force": + case "-f": + force = true; + break; + case "--linux": + case "-l": + runtime = "linux-x64"; + break; + case "--mac": + case "-m": + runtime = "osx-arm64"; + break; + case "--win": + case "-w": + runtime = "win-x64"; + break; + case "--allplatforms": + allPlatforms = true; + break; + case "--help": + case "-h": + Console.WriteLine("Usage: dotnet ScriptBuilder.cs [options] [script1.cs script2.cs ...]"); + Console.WriteLine("Options:"); + Console.WriteLine(" --exclude Exclude listed scripts instead of including them"); + Console.WriteLine(" --force, -f Force rebuild of all scripts"); + Console.WriteLine(" --linux, -l Build for Linux platform"); + Console.WriteLine(" --mac, -m Build for macOS platform"); + Console.WriteLine(" --win, -w Build for Windows platform"); + Console.WriteLine(" --allPlatforms Build for all platforms"); + Console.WriteLine(" --help, -h Show this help message"); + return 0; + default: + scriptsToProcess.Add(arg); + break; + } +} + +string currentBranch = GetCurrentGitBranch(coreDir); + +int result = -1; + +if (allPlatforms) +{ + string[] platforms = new[] { "linux-x64", "osx-arm64", "win-x64" }; + foreach (string plat in platforms) + { + Trace.WriteLine($"Building for platform: {plat}"); + result = BuildScripts(scripts, scriptsToProcess, coreDir, plat, force, scriptsDir, excludeMode, currentBranch); + if (result != 0) + { + break; + } + } +} +else if (runtime == "win-x64" && RuntimeInformation.OSArchitecture != Architecture.X64) +{ + Trace.WriteLine("Warning: Building for win-x64 on non-x64 architecture may fail."); + result = BuildScripts(scripts, scriptsToProcess, coreDir, runtime, force, scriptsDir, excludeMode, currentBranch); +} +else if (runtime == "osx-arm64" && RuntimeInformation.OSArchitecture != Architecture.Arm64) +{ + Trace.WriteLine("Warning: Building for osx-arm64 on non-Arm64 architecture may fail."); + result = BuildScripts(scripts, scriptsToProcess, coreDir, runtime, force, scriptsDir, excludeMode, currentBranch); +} +else if (runtime == "linux-x64" && RuntimeInformation.OSArchitecture != Architecture.X64) +{ + Trace.WriteLine("Warning: Building for linux-x64 on non-x64 architecture may fail."); + result = BuildScripts(scripts, scriptsToProcess, coreDir, runtime, force, scriptsDir, excludeMode, currentBranch); +} +else +{ + result = BuildScripts(scripts, scriptsToProcess, coreDir, runtime, force, scriptsDir, excludeMode, currentBranch); +} + +if (result != 0) +{ + Trace.WriteLine($"ScriptBuilder failed with exit code {result}"); + return result; +} + +return 0; + +static int BuildScripts(string[] scripts, HashSet scriptsToProcess, string coreDir, string runtime, bool force, + string scriptsDir, bool excludeMode, string currentBranch) +{ + string outDir = Path.GetFullPath(Path.Combine(coreDir, "build-tools", runtime)); + Trace.WriteLine($"Output directory: {outDir}"); + + Directory.CreateDirectory(outDir); + + string recordFile = Path.Combine(outDir, ".csbuild-record.json"); + (long timeStamp, string oldBranch) = GetLastBuildRecord(recordFile); + bool branchChanged = oldBranch != currentBranch; + + if (scriptsToProcess.Count > 0) + { + Trace.WriteLine(excludeMode + ? $"Excluding specified scripts: {string.Join(", ", scriptsToProcess)}" + : $"Including only specified scripts: {string.Join(", ", scriptsToProcess)}"); + } + + foreach (string script in scripts) + { + if (Path.GetFileName(script) == "ScriptBuilder.cs") + { + continue; + } + + if (scriptsToProcess.Count > 0) + { + if (excludeMode && scriptsToProcess.Contains(Path.GetFileName(script))) + { + Trace.WriteLine($"Skipping excluded script: {Path.GetFileName(script)}"); + continue; + } + else if (!excludeMode && !scriptsToProcess.Contains(Path.GetFileName(script))) + { + Trace.WriteLine($"Skipping unlisted script: {Path.GetFileName(script)}"); + continue; + } + } + + if (!branchChanged && !CheckIfNeedsBuild(timeStamp, script, outDir)) + { + Trace.WriteLine($"Skipping unchanged script: {Path.GetFileName(script)}"); + continue; + } + + int returnCode = BuildScript(Path.GetFileName(script), scriptsDir, outDir, runtime); + if (returnCode != 0) + { + return returnCode; + } + } + + SaveBuildRecord(recordFile, currentBranch); + + return 0; +} + +/// +/// Compiles a single C# script to a DLL using 'dotnet build'. +/// +/// The name of the script file (e.g., "ESBuild.cs"). +/// The directory containing the script. +/// The output directory for the compiled DLL. +/// The runtime identifier for the build. +/// 0 on success, non-zero on failure. +static int BuildScript(string scriptName, string scriptsDir, string outDir, string runtime) +{ + string[] args = + [ + "build", + scriptName, + "-c", + "Release", + "-o", + outDir, + "--runtime", + runtime + ]; + + ProcessStartInfo psi = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = string.Join(" ", args), + WorkingDirectory = scriptsDir, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false + }; + + using Process? process = Process.Start(psi); + if (process is null) + { + Trace.WriteLine($"Failed to build {scriptName}"); + return 1; + } + + // Read output asynchronously + process.OutputDataReceived += (sender, e) => + { + if (e.Data is not null) + { + Trace.WriteLine(e.Data); + } + }; + + process.ErrorDataReceived += (sender, e) => + { + if (e.Data is not null) + { + Trace.WriteLine(e.Data); + } + }; + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.WaitForExit(); + return process.ExitCode; +} + +/// +/// Gets the current Git branch name for a repository. +/// +/// The directory within the Git repository. +/// The branch name, or "unknown" if Git is unavailable or fails. +static string GetCurrentGitBranch(string workingDirectory) +{ + try + { + var psi = new ProcessStartInfo + { + FileName = "git", + Arguments = "rev-parse --abbrev-ref HEAD", + WorkingDirectory = workingDirectory, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + using var process = Process.Start(psi); + if (process is null) return "unknown"; + + string output = process.StandardOutput.ReadToEnd().Trim(); + process.WaitForExit(); + + return process.ExitCode == 0 ? output : "unknown"; + } + catch + { + return "unknown"; + } +} + +/// +/// Determines whether a build is needed. +/// A build is needed if the script was modified since last build, +/// or the output directory is empty. +/// +/// The timestamp of the last build. +/// The script name to check for changes. +/// Path to the JavaScript output directory. +/// True if a build should be performed. +static bool CheckIfNeedsBuild(long timeStamp, string script, string outputDir) +{ + if (!GetScriptModifiedSince(script, timeStamp)) + { + Trace.WriteLine("No change in script since last build."); + + // Check output directory for existing files + if (Directory.Exists(outputDir) && Directory.GetFiles(outputDir).Length > 0) + { + // DLLs and runtimeconfig.json files must exist for each script to function in build pipelines + string fileName = Path.GetFileNameWithoutExtension(script); + if (fileName == "ScriptBuilder") + { + // we always skip ScriptBuilder itself + return false; + } + string outputDll = Path.Combine(outputDir, fileName + ".dll"); + if (!File.Exists(outputDll)) + { + Trace.WriteLine($"Output DLL missing: {outputDll}. Proceeding with build."); + return true; + } + string outputRuntimeJson = Path.Combine(outputDir, fileName + ".runtimeconfig.json"); + if (!File.Exists(outputRuntimeJson)) + { + Trace.WriteLine($"Output runtime config missing: {outputRuntimeJson}. Proceeding with build."); + return true; + } + } + else + { + Trace.WriteLine("Output directory is empty. Proceeding with build."); + return true; + } + } + + Trace.WriteLine("Changes detected in Scripts folder. Proceeding with build."); + return true; +} + +/// +/// Reads the last build record from the JSON file. +/// The record contains the timestamp of the last successful build and the branch name. +/// +/// Path to the .esbuild-record.json file. +/// A tuple containing the Unix timestamp (milliseconds) and branch name. +static (long Timestamp, string Branch) GetLastBuildRecord(string recordFilePath) +{ + if (!File.Exists(recordFilePath)) + { + return (0, "unknown"); + } + + try + { + string json = File.ReadAllText(recordFilePath); + using var doc = JsonDocument.Parse(json); + var root = doc.RootElement; + + long timestamp = root.TryGetProperty("timestamp", out var ts) ? ts.GetInt64() : 0; + string branch = root.TryGetProperty("branch", out var br) ? br.GetString() ?? "unknown" : "unknown"; + + return (timestamp, branch); + } + catch + { + return (0, "unknown"); + } +} + +/// +/// Checks if the script is newer than the compiled tool +/// +/// Path to the source script. +/// Unix timestamp (milliseconds) of the last build. +/// True if the file has been modified since the timestamp. +static bool GetScriptModifiedSince(string script, long lastTimestamp) +{ + var lastBuildTime = DateTimeOffset.FromUnixTimeMilliseconds(lastTimestamp).DateTime; + + return File.GetLastWriteTimeUtc(script) > lastBuildTime; +} + +/// +/// Saves the build record to a JSON file after a successful build. +/// Records the current timestamp and branch name for incremental build detection. +/// +/// Path where the record should be saved. +/// The current Git branch name. +static void SaveBuildRecord(string recordFilePath, string branch) +{ + long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + // Write JSON manually to avoid reflection-based serialization (not compatible with Native AOT) + string json = $$""" + { + "timestamp": {{timestamp}}, + "branch": "{{branch.Replace("\\", "\\\\").Replace("\"", "\\\"")}}" + } + """; + File.WriteAllText(recordFilePath, json); +} + +/// +/// Gets the relative directory containing the build scripts. +/// +static string GetScriptsDirectory([CallerFilePath] string? callerFilePath = null) +{ + string dllDirectory = AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + if (dllDirectory.Contains("dotnet")) + { + // we are running from the C# script in build-scripts, so we can use the caller file path to find the script directory + return Path.GetDirectoryName(callerFilePath!)!; + } + + // otherwise the dll is stored in ./build-tools/{os}-{arch} + return Path.GetFullPath(Path.Combine(dllDirectory, "..", "build-scripts")); +} \ No newline at end of file diff --git a/build-tools/build-scripts/runtimeconfig.template.json b/build-tools/build-scripts/runtimeconfig.template.json new file mode 100644 index 000000000..835174faf --- /dev/null +++ b/build-tools/build-scripts/runtimeconfig.template.json @@ -0,0 +1,8 @@ +{ + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + } +} \ No newline at end of file diff --git a/build-tools/BuildAppSettings b/build-tools/linux-x64/BuildAppSettings similarity index 100% rename from build-tools/BuildAppSettings rename to build-tools/linux-x64/BuildAppSettings diff --git a/build-tools/linux-x64/BuildAppSettings.deps.json b/build-tools/linux-x64/BuildAppSettings.deps.json new file mode 100644 index 000000000..1e3059c46 --- /dev/null +++ b/build-tools/linux-x64/BuildAppSettings.deps.json @@ -0,0 +1,24 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/linux-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/linux-x64": { + "BuildAppSettings/1.0.0": { + "runtime": { + "BuildAppSettings.dll": {} + } + } + } + }, + "libraries": { + "BuildAppSettings/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/BuildAppSettings.dll b/build-tools/linux-x64/BuildAppSettings.dll similarity index 73% rename from build-tools/BuildAppSettings.dll rename to build-tools/linux-x64/BuildAppSettings.dll index b7ae08138..95b942a48 100644 Binary files a/build-tools/BuildAppSettings.dll and b/build-tools/linux-x64/BuildAppSettings.dll differ diff --git a/build-tools/linux-x64/BuildAppSettings.runtimeconfig.json b/build-tools/linux-x64/BuildAppSettings.runtimeconfig.json new file mode 100644 index 000000000..37ed606d1 --- /dev/null +++ b/build-tools/linux-x64/BuildAppSettings.runtimeconfig.json @@ -0,0 +1,43 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts/BuildAppSettings.cs", + "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.Security.UseManagedNtlm": false, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/BuildTemplates b/build-tools/linux-x64/BuildTemplates similarity index 100% rename from build-tools/BuildTemplates rename to build-tools/linux-x64/BuildTemplates diff --git a/build-tools/linux-x64/BuildTemplates.deps.json b/build-tools/linux-x64/BuildTemplates.deps.json new file mode 100644 index 000000000..2f9b491fa --- /dev/null +++ b/build-tools/linux-x64/BuildTemplates.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/linux-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/linux-x64": { + "BuildTemplates/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "BuildTemplates.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "BuildTemplates/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/linux-x64/BuildTemplates.dll b/build-tools/linux-x64/BuildTemplates.dll new file mode 100644 index 000000000..2952335bb Binary files /dev/null and b/build-tools/linux-x64/BuildTemplates.dll differ diff --git a/build-tools/BuildTemplates.runtimeconfig.json b/build-tools/linux-x64/BuildTemplates.runtimeconfig.json similarity index 65% rename from build-tools/BuildTemplates.runtimeconfig.json rename to build-tools/linux-x64/BuildTemplates.runtimeconfig.json index 97080ffdb..649bd27c5 100644 --- a/build-tools/BuildTemplates.runtimeconfig.json +++ b/build-tools/linux-x64/BuildTemplates.runtimeconfig.json @@ -5,9 +5,15 @@ "name": "Microsoft.NETCore.App", "version": "10.0.0" }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, "configProperties": { - "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts/BuildTemplates.cs", - "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts", + "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts/BuildTemplates.cs", + "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts", "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false } diff --git a/build-tools/linux-x64/CliWrap.dll b/build-tools/linux-x64/CliWrap.dll new file mode 100755 index 000000000..3996990be Binary files /dev/null and b/build-tools/linux-x64/CliWrap.dll differ diff --git a/build-tools/ConsoleDialog b/build-tools/linux-x64/ConsoleDialog similarity index 100% rename from build-tools/ConsoleDialog rename to build-tools/linux-x64/ConsoleDialog diff --git a/build-tools/linux-x64/ConsoleDialog.deps.json b/build-tools/linux-x64/ConsoleDialog.deps.json new file mode 100644 index 000000000..066182f82 --- /dev/null +++ b/build-tools/linux-x64/ConsoleDialog.deps.json @@ -0,0 +1,24 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/linux-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/linux-x64": { + "ConsoleDialog/1.0.0": { + "runtime": { + "ConsoleDialog.dll": {} + } + } + } + }, + "libraries": { + "ConsoleDialog/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/linux-x64/ConsoleDialog.dll b/build-tools/linux-x64/ConsoleDialog.dll new file mode 100644 index 000000000..032fb3525 Binary files /dev/null and b/build-tools/linux-x64/ConsoleDialog.dll differ diff --git a/build-tools/ESBuildClearLocks.runtimeconfig.json b/build-tools/linux-x64/ConsoleDialog.runtimeconfig.json similarity index 87% rename from build-tools/ESBuildClearLocks.runtimeconfig.json rename to build-tools/linux-x64/ConsoleDialog.runtimeconfig.json index 555ae6101..51192fb4c 100644 --- a/build-tools/ESBuildClearLocks.runtimeconfig.json +++ b/build-tools/linux-x64/ConsoleDialog.runtimeconfig.json @@ -5,9 +5,15 @@ "name": "Microsoft.NETCore.App", "version": "10.0.0" }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, "configProperties": { - "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts/ESBuildClearLocks.cs", - "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts", + "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts/ConsoleDialog.cs", + "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts", "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, "System.ComponentModel.DefaultValueAttribute.IsSupported": false, "System.ComponentModel.Design.IDesignerHost.IsSupported": false, @@ -16,6 +22,7 @@ "System.Data.DataSet.XmlSerializationIsSupported": false, "System.Diagnostics.Tracing.EventSource.IsSupported": false, "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.Security.UseManagedNtlm": false, "System.Net.SocketsHttpHandler.Http3Support": false, "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, "System.Resources.ResourceManager.AllowCustomResourceTypes": false, diff --git a/build-tools/ESBuild b/build-tools/linux-x64/ESBuild similarity index 100% rename from build-tools/ESBuild rename to build-tools/linux-x64/ESBuild diff --git a/build-tools/linux-x64/ESBuild.deps.json b/build-tools/linux-x64/ESBuild.deps.json new file mode 100644 index 000000000..1b26f4be4 --- /dev/null +++ b/build-tools/linux-x64/ESBuild.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/linux-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/linux-x64": { + "ESBuild/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "ESBuild.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "ESBuild/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/linux-x64/ESBuild.dll b/build-tools/linux-x64/ESBuild.dll new file mode 100644 index 000000000..08a551630 Binary files /dev/null and b/build-tools/linux-x64/ESBuild.dll differ diff --git a/build-tools/BuildAppSettings.runtimeconfig.json b/build-tools/linux-x64/ESBuild.runtimeconfig.json similarity index 87% rename from build-tools/BuildAppSettings.runtimeconfig.json rename to build-tools/linux-x64/ESBuild.runtimeconfig.json index 6766cb69d..38c4501b6 100644 --- a/build-tools/BuildAppSettings.runtimeconfig.json +++ b/build-tools/linux-x64/ESBuild.runtimeconfig.json @@ -5,9 +5,15 @@ "name": "Microsoft.NETCore.App", "version": "10.0.0" }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, "configProperties": { - "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts/BuildAppSettings.cs", - "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts", + "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts/ESBuild.cs", + "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts", "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, "System.ComponentModel.DefaultValueAttribute.IsSupported": false, "System.ComponentModel.Design.IDesignerHost.IsSupported": false, @@ -16,6 +22,7 @@ "System.Data.DataSet.XmlSerializationIsSupported": false, "System.Diagnostics.Tracing.EventSource.IsSupported": false, "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.Security.UseManagedNtlm": false, "System.Net.SocketsHttpHandler.Http3Support": false, "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, "System.Resources.ResourceManager.AllowCustomResourceTypes": false, diff --git a/build-tools/ESBuildClearLocks b/build-tools/linux-x64/ESBuildClearLocks similarity index 100% rename from build-tools/ESBuildClearLocks rename to build-tools/linux-x64/ESBuildClearLocks diff --git a/build-tools/linux-x64/ESBuildClearLocks.deps.json b/build-tools/linux-x64/ESBuildClearLocks.deps.json new file mode 100644 index 000000000..e01a173b2 --- /dev/null +++ b/build-tools/linux-x64/ESBuildClearLocks.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/linux-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/linux-x64": { + "ESBuildClearLocks/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "ESBuildClearLocks.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "ESBuildClearLocks/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/linux-x64/ESBuildClearLocks.dll b/build-tools/linux-x64/ESBuildClearLocks.dll new file mode 100644 index 000000000..abfa93d26 Binary files /dev/null and b/build-tools/linux-x64/ESBuildClearLocks.dll differ diff --git a/build-tools/linux-x64/ESBuildClearLocks.runtimeconfig.json b/build-tools/linux-x64/ESBuildClearLocks.runtimeconfig.json new file mode 100644 index 000000000..da6fa6890 --- /dev/null +++ b/build-tools/linux-x64/ESBuildClearLocks.runtimeconfig.json @@ -0,0 +1,43 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts/ESBuildClearLocks.cs", + "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.Security.UseManagedNtlm": false, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/FetchNuGetVersion b/build-tools/linux-x64/FetchNuGetVersion similarity index 100% rename from build-tools/FetchNuGetVersion rename to build-tools/linux-x64/FetchNuGetVersion diff --git a/build-tools/linux-x64/FetchNuGetVersion.deps.json b/build-tools/linux-x64/FetchNuGetVersion.deps.json new file mode 100644 index 000000000..21877df70 --- /dev/null +++ b/build-tools/linux-x64/FetchNuGetVersion.deps.json @@ -0,0 +1,24 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/linux-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/linux-x64": { + "FetchNuGetVersion/1.0.0": { + "runtime": { + "FetchNuGetVersion.dll": {} + } + } + } + }, + "libraries": { + "FetchNuGetVersion/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/linux-x64/FetchNuGetVersion.dll b/build-tools/linux-x64/FetchNuGetVersion.dll new file mode 100644 index 000000000..ad6a822ce Binary files /dev/null and b/build-tools/linux-x64/FetchNuGetVersion.dll differ diff --git a/build-tools/FetchNuGetVersion.runtimeconfig.json b/build-tools/linux-x64/FetchNuGetVersion.runtimeconfig.json similarity index 87% rename from build-tools/FetchNuGetVersion.runtimeconfig.json rename to build-tools/linux-x64/FetchNuGetVersion.runtimeconfig.json index a1cdfde9a..131a45746 100644 --- a/build-tools/FetchNuGetVersion.runtimeconfig.json +++ b/build-tools/linux-x64/FetchNuGetVersion.runtimeconfig.json @@ -5,9 +5,15 @@ "name": "Microsoft.NETCore.App", "version": "10.0.0" }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, "configProperties": { - "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts/FetchNuGetVersion.cs", - "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts", + "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts/FetchNuGetVersion.cs", + "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts", "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, "System.ComponentModel.DefaultValueAttribute.IsSupported": false, "System.ComponentModel.Design.IDesignerHost.IsSupported": false, @@ -16,6 +22,7 @@ "System.Data.DataSet.XmlSerializationIsSupported": false, "System.Diagnostics.Tracing.EventSource.IsSupported": false, "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.Security.UseManagedNtlm": false, "System.Net.SocketsHttpHandler.Http3Support": false, "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, "System.Resources.ResourceManager.AllowCustomResourceTypes": false, diff --git a/build-tools/ESBuildWaitForCompletion b/build-tools/linux-x64/GBTest similarity index 99% rename from build-tools/ESBuildWaitForCompletion rename to build-tools/linux-x64/GBTest index 61b724649..9b0d563e7 100755 Binary files a/build-tools/ESBuildWaitForCompletion and b/build-tools/linux-x64/GBTest differ diff --git a/build-tools/linux-x64/GBTest.deps.json b/build-tools/linux-x64/GBTest.deps.json new file mode 100644 index 000000000..9104bfbac --- /dev/null +++ b/build-tools/linux-x64/GBTest.deps.json @@ -0,0 +1,56 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/linux-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/linux-x64": { + "GBTest/1.0.0": { + "dependencies": { + "CliWrap": "3.10.0", + "Utilities": "1.0.0" + }, + "runtime": { + "GBTest.dll": {} + } + }, + "CliWrap/3.10.0": { + "runtime": { + "lib/net10.0/CliWrap.dll": { + "assemblyVersion": "3.10.0.0", + "fileVersion": "3.10.0.0" + } + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "GBTest/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "CliWrap/3.10.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Igph39WNImUFqQrpTeBDPafcCnM1u/8IooZHqMRF/5JdV5H78p+AbZkeYY1Nkz1WlbI/Gt2Hq/3rYh55jxRF9Q==", + "path": "cliwrap/3.10.0", + "hashPath": "cliwrap.3.10.0.nupkg.sha512" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/linux-x64/GBTest.dll b/build-tools/linux-x64/GBTest.dll new file mode 100644 index 000000000..3c4b2d934 Binary files /dev/null and b/build-tools/linux-x64/GBTest.dll differ diff --git a/build-tools/ESBuild.runtimeconfig.json b/build-tools/linux-x64/GBTest.runtimeconfig.json similarity index 87% rename from build-tools/ESBuild.runtimeconfig.json rename to build-tools/linux-x64/GBTest.runtimeconfig.json index 3b38c9acb..f923bd398 100644 --- a/build-tools/ESBuild.runtimeconfig.json +++ b/build-tools/linux-x64/GBTest.runtimeconfig.json @@ -5,9 +5,15 @@ "name": "Microsoft.NETCore.App", "version": "10.0.0" }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, "configProperties": { - "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts/ESBuild.cs", - "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts", + "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts/GBTest.cs", + "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts", "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, "System.ComponentModel.DefaultValueAttribute.IsSupported": false, "System.ComponentModel.Design.IDesignerHost.IsSupported": false, @@ -16,6 +22,7 @@ "System.Data.DataSet.XmlSerializationIsSupported": false, "System.Diagnostics.Tracing.EventSource.IsSupported": false, "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.Security.UseManagedNtlm": false, "System.Net.SocketsHttpHandler.Http3Support": false, "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, "System.Resources.ResourceManager.AllowCustomResourceTypes": false, diff --git a/build-tools/GeoBlazorBuild b/build-tools/linux-x64/GeoBlazorBuild similarity index 100% rename from build-tools/GeoBlazorBuild rename to build-tools/linux-x64/GeoBlazorBuild diff --git a/build-tools/linux-x64/GeoBlazorBuild.deps.json b/build-tools/linux-x64/GeoBlazorBuild.deps.json new file mode 100644 index 000000000..ff37013ce --- /dev/null +++ b/build-tools/linux-x64/GeoBlazorBuild.deps.json @@ -0,0 +1,56 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/linux-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/linux-x64": { + "GeoBlazorBuild/1.0.0": { + "dependencies": { + "Polly.Core": "8.6.5", + "Utilities": "1.0.0" + }, + "runtime": { + "GeoBlazorBuild.dll": {} + } + }, + "Polly.Core/8.6.5": { + "runtime": { + "lib/net8.0/Polly.Core.dll": { + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.6.5.5194" + } + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "GeoBlazorBuild/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Polly.Core/8.6.5": { + "type": "package", + "serviceable": true, + "sha512": "sha512-t+sUVrIwvo7UmsgHGgOG9F0GDZSRIm47u2ylH17Gvcv1q5hNEwgD5GoBlFyc0kh/pebmPyrAgvGsR/65ZBaXlg==", + "path": "polly.core/8.6.5", + "hashPath": "polly.core.8.6.5.nupkg.sha512" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/linux-x64/GeoBlazorBuild.dll b/build-tools/linux-x64/GeoBlazorBuild.dll new file mode 100644 index 000000000..4d1bdc535 Binary files /dev/null and b/build-tools/linux-x64/GeoBlazorBuild.dll differ diff --git a/build-tools/linux-x64/GeoBlazorBuild.runtimeconfig.json b/build-tools/linux-x64/GeoBlazorBuild.runtimeconfig.json new file mode 100644 index 000000000..5f1eb5354 --- /dev/null +++ b/build-tools/linux-x64/GeoBlazorBuild.runtimeconfig.json @@ -0,0 +1,43 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\GeoBlazorBuild.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.Security.UseManagedNtlm": false, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/linux-x64/Polly.Core.dll b/build-tools/linux-x64/Polly.Core.dll new file mode 100755 index 000000000..57eae2055 Binary files /dev/null and b/build-tools/linux-x64/Polly.Core.dll differ diff --git a/build-tools/linux-x64/RazorCopy b/build-tools/linux-x64/RazorCopy new file mode 100755 index 000000000..9c59a3ed6 Binary files /dev/null and b/build-tools/linux-x64/RazorCopy differ diff --git a/build-tools/linux-x64/RazorCopy.deps.json b/build-tools/linux-x64/RazorCopy.deps.json new file mode 100644 index 000000000..7f5d93b32 --- /dev/null +++ b/build-tools/linux-x64/RazorCopy.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/linux-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/linux-x64": { + "RazorCopy/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "RazorCopy.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "RazorCopy/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/linux-x64/RazorCopy.dll b/build-tools/linux-x64/RazorCopy.dll new file mode 100644 index 000000000..a92417c1d Binary files /dev/null and b/build-tools/linux-x64/RazorCopy.dll differ diff --git a/build-tools/ConsoleDialog.runtimeconfig.json b/build-tools/linux-x64/RazorCopy.runtimeconfig.json similarity index 87% rename from build-tools/ConsoleDialog.runtimeconfig.json rename to build-tools/linux-x64/RazorCopy.runtimeconfig.json index 033cf94ea..ab5d19006 100644 --- a/build-tools/ConsoleDialog.runtimeconfig.json +++ b/build-tools/linux-x64/RazorCopy.runtimeconfig.json @@ -5,9 +5,15 @@ "name": "Microsoft.NETCore.App", "version": "10.0.0" }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, "configProperties": { - "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts/ConsoleDialog.cs", - "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts", + "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts/RazorCopy.cs", + "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-tools/build-scripts", "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, "System.ComponentModel.DefaultValueAttribute.IsSupported": false, "System.ComponentModel.Design.IDesignerHost.IsSupported": false, @@ -16,6 +22,7 @@ "System.Data.DataSet.XmlSerializationIsSupported": false, "System.Diagnostics.Tracing.EventSource.IsSupported": false, "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.Security.UseManagedNtlm": false, "System.Net.SocketsHttpHandler.Http3Support": false, "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, "System.Resources.ResourceManager.AllowCustomResourceTypes": false, diff --git a/build-tools/ESBuild.deps.json b/build-tools/linux-x64/Utilities.deps.json similarity index 79% rename from build-tools/ESBuild.deps.json rename to build-tools/linux-x64/Utilities.deps.json index a0835a4a7..88bf821d5 100644 --- a/build-tools/ESBuild.deps.json +++ b/build-tools/linux-x64/Utilities.deps.json @@ -6,15 +6,15 @@ "compilationOptions": {}, "targets": { ".NETCoreApp,Version=v10.0": { - "ESBuild/1.0.0": { + "Utilities/1.0.0": { "runtime": { - "ESBuild.dll": {} + "Utilities.dll": {} } } } }, "libraries": { - "ESBuild/1.0.0": { + "Utilities/1.0.0": { "type": "project", "serviceable": false, "sha512": "" diff --git a/build-tools/linux-x64/Utilities.dll b/build-tools/linux-x64/Utilities.dll new file mode 100644 index 000000000..8baf0b956 Binary files /dev/null and b/build-tools/linux-x64/Utilities.dll differ diff --git a/build-tools/osx-arm64/BuildAppSettings b/build-tools/osx-arm64/BuildAppSettings new file mode 100755 index 000000000..fd1c42355 Binary files /dev/null and b/build-tools/osx-arm64/BuildAppSettings differ diff --git a/build-tools/osx-arm64/BuildAppSettings.deps.json b/build-tools/osx-arm64/BuildAppSettings.deps.json new file mode 100644 index 000000000..52bd99934 --- /dev/null +++ b/build-tools/osx-arm64/BuildAppSettings.deps.json @@ -0,0 +1,24 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/osx-arm64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/osx-arm64": { + "BuildAppSettings/1.0.0": { + "runtime": { + "BuildAppSettings.dll": {} + } + } + } + }, + "libraries": { + "BuildAppSettings/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/BuildAppSettings.dll b/build-tools/osx-arm64/BuildAppSettings.dll new file mode 100644 index 000000000..0aec0d623 Binary files /dev/null and b/build-tools/osx-arm64/BuildAppSettings.dll differ diff --git a/build-tools/osx-arm64/BuildAppSettings.runtimeconfig.json b/build-tools/osx-arm64/BuildAppSettings.runtimeconfig.json new file mode 100644 index 000000000..059210063 --- /dev/null +++ b/build-tools/osx-arm64/BuildAppSettings.runtimeconfig.json @@ -0,0 +1,42 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts/BuildAppSettings.cs", + "EntryPointFileDirectoryPath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/BuildTemplates b/build-tools/osx-arm64/BuildTemplates new file mode 100755 index 000000000..30482914e Binary files /dev/null and b/build-tools/osx-arm64/BuildTemplates differ diff --git a/build-tools/osx-arm64/BuildTemplates.deps.json b/build-tools/osx-arm64/BuildTemplates.deps.json new file mode 100644 index 000000000..7ce1d2529 --- /dev/null +++ b/build-tools/osx-arm64/BuildTemplates.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/osx-arm64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/osx-arm64": { + "BuildTemplates/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "BuildTemplates.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "BuildTemplates/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/BuildTemplates.dll b/build-tools/osx-arm64/BuildTemplates.dll new file mode 100644 index 000000000..8de416063 Binary files /dev/null and b/build-tools/osx-arm64/BuildTemplates.dll differ diff --git a/build-tools/osx-arm64/BuildTemplates.runtimeconfig.json b/build-tools/osx-arm64/BuildTemplates.runtimeconfig.json new file mode 100644 index 000000000..cf7692b83 --- /dev/null +++ b/build-tools/osx-arm64/BuildTemplates.runtimeconfig.json @@ -0,0 +1,21 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts/BuildTemplates.cs", + "EntryPointFileDirectoryPath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts", + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/CliWrap.dll b/build-tools/osx-arm64/CliWrap.dll new file mode 100755 index 000000000..3996990be Binary files /dev/null and b/build-tools/osx-arm64/CliWrap.dll differ diff --git a/build-tools/osx-arm64/ConsoleDialog b/build-tools/osx-arm64/ConsoleDialog new file mode 100755 index 000000000..223e5ee10 Binary files /dev/null and b/build-tools/osx-arm64/ConsoleDialog differ diff --git a/build-tools/osx-arm64/ConsoleDialog.deps.json b/build-tools/osx-arm64/ConsoleDialog.deps.json new file mode 100644 index 000000000..d576a2e37 --- /dev/null +++ b/build-tools/osx-arm64/ConsoleDialog.deps.json @@ -0,0 +1,24 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/osx-arm64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/osx-arm64": { + "ConsoleDialog/1.0.0": { + "runtime": { + "ConsoleDialog.dll": {} + } + } + } + }, + "libraries": { + "ConsoleDialog/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/ConsoleDialog.dll b/build-tools/osx-arm64/ConsoleDialog.dll new file mode 100644 index 000000000..5bb148fdd Binary files /dev/null and b/build-tools/osx-arm64/ConsoleDialog.dll differ diff --git a/build-tools/osx-arm64/ConsoleDialog.runtimeconfig.json b/build-tools/osx-arm64/ConsoleDialog.runtimeconfig.json new file mode 100644 index 000000000..2c4ca27df --- /dev/null +++ b/build-tools/osx-arm64/ConsoleDialog.runtimeconfig.json @@ -0,0 +1,42 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts/ConsoleDialog.cs", + "EntryPointFileDirectoryPath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/ESBuild b/build-tools/osx-arm64/ESBuild new file mode 100755 index 000000000..f7d96e3b0 Binary files /dev/null and b/build-tools/osx-arm64/ESBuild differ diff --git a/build-tools/osx-arm64/ESBuild.deps.json b/build-tools/osx-arm64/ESBuild.deps.json new file mode 100644 index 000000000..6f00c5ac8 --- /dev/null +++ b/build-tools/osx-arm64/ESBuild.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/osx-arm64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/osx-arm64": { + "ESBuild/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "ESBuild.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "ESBuild/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/ESBuild.dll b/build-tools/osx-arm64/ESBuild.dll new file mode 100644 index 000000000..5e1907551 Binary files /dev/null and b/build-tools/osx-arm64/ESBuild.dll differ diff --git a/build-tools/osx-arm64/ESBuild.runtimeconfig.json b/build-tools/osx-arm64/ESBuild.runtimeconfig.json new file mode 100644 index 000000000..d1930f8c3 --- /dev/null +++ b/build-tools/osx-arm64/ESBuild.runtimeconfig.json @@ -0,0 +1,42 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts/ESBuild.cs", + "EntryPointFileDirectoryPath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/ESBuildClearLocks b/build-tools/osx-arm64/ESBuildClearLocks new file mode 100755 index 000000000..72c68fc8c Binary files /dev/null and b/build-tools/osx-arm64/ESBuildClearLocks differ diff --git a/build-tools/osx-arm64/ESBuildClearLocks.deps.json b/build-tools/osx-arm64/ESBuildClearLocks.deps.json new file mode 100644 index 000000000..79cc98230 --- /dev/null +++ b/build-tools/osx-arm64/ESBuildClearLocks.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/osx-arm64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/osx-arm64": { + "ESBuildClearLocks/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "ESBuildClearLocks.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "ESBuildClearLocks/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/ESBuildClearLocks.dll b/build-tools/osx-arm64/ESBuildClearLocks.dll new file mode 100644 index 000000000..c111a9e49 Binary files /dev/null and b/build-tools/osx-arm64/ESBuildClearLocks.dll differ diff --git a/build-tools/osx-arm64/ESBuildClearLocks.runtimeconfig.json b/build-tools/osx-arm64/ESBuildClearLocks.runtimeconfig.json new file mode 100644 index 000000000..075d7dc73 --- /dev/null +++ b/build-tools/osx-arm64/ESBuildClearLocks.runtimeconfig.json @@ -0,0 +1,42 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts/ESBuildClearLocks.cs", + "EntryPointFileDirectoryPath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/FetchNuGetVersion b/build-tools/osx-arm64/FetchNuGetVersion new file mode 100755 index 000000000..54978cfe6 Binary files /dev/null and b/build-tools/osx-arm64/FetchNuGetVersion differ diff --git a/build-tools/osx-arm64/FetchNuGetVersion.deps.json b/build-tools/osx-arm64/FetchNuGetVersion.deps.json new file mode 100644 index 000000000..ddc928dbf --- /dev/null +++ b/build-tools/osx-arm64/FetchNuGetVersion.deps.json @@ -0,0 +1,24 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/osx-arm64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/osx-arm64": { + "FetchNuGetVersion/1.0.0": { + "runtime": { + "FetchNuGetVersion.dll": {} + } + } + } + }, + "libraries": { + "FetchNuGetVersion/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/FetchNuGetVersion.dll b/build-tools/osx-arm64/FetchNuGetVersion.dll similarity index 69% rename from build-tools/FetchNuGetVersion.dll rename to build-tools/osx-arm64/FetchNuGetVersion.dll index 152c8ebf0..fe009e686 100644 Binary files a/build-tools/FetchNuGetVersion.dll and b/build-tools/osx-arm64/FetchNuGetVersion.dll differ diff --git a/build-tools/osx-arm64/FetchNuGetVersion.runtimeconfig.json b/build-tools/osx-arm64/FetchNuGetVersion.runtimeconfig.json new file mode 100644 index 000000000..76488907e --- /dev/null +++ b/build-tools/osx-arm64/FetchNuGetVersion.runtimeconfig.json @@ -0,0 +1,42 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts/FetchNuGetVersion.cs", + "EntryPointFileDirectoryPath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/GBTest b/build-tools/osx-arm64/GBTest new file mode 100755 index 000000000..f9916e039 Binary files /dev/null and b/build-tools/osx-arm64/GBTest differ diff --git a/build-tools/osx-arm64/GBTest.deps.json b/build-tools/osx-arm64/GBTest.deps.json new file mode 100644 index 000000000..c1a2e4566 --- /dev/null +++ b/build-tools/osx-arm64/GBTest.deps.json @@ -0,0 +1,56 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/osx-arm64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/osx-arm64": { + "GBTest/1.0.0": { + "dependencies": { + "CliWrap": "3.10.0", + "Utilities": "1.0.0" + }, + "runtime": { + "GBTest.dll": {} + } + }, + "CliWrap/3.10.0": { + "runtime": { + "lib/net10.0/CliWrap.dll": { + "assemblyVersion": "3.10.0.0", + "fileVersion": "3.10.0.0" + } + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "GBTest/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "CliWrap/3.10.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Igph39WNImUFqQrpTeBDPafcCnM1u/8IooZHqMRF/5JdV5H78p+AbZkeYY1Nkz1WlbI/Gt2Hq/3rYh55jxRF9Q==", + "path": "cliwrap/3.10.0", + "hashPath": "cliwrap.3.10.0.nupkg.sha512" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/GBTest.dll b/build-tools/osx-arm64/GBTest.dll new file mode 100644 index 000000000..a4df24700 Binary files /dev/null and b/build-tools/osx-arm64/GBTest.dll differ diff --git a/build-tools/osx-arm64/GBTest.runtimeconfig.json b/build-tools/osx-arm64/GBTest.runtimeconfig.json new file mode 100644 index 000000000..c0db3c7b0 --- /dev/null +++ b/build-tools/osx-arm64/GBTest.runtimeconfig.json @@ -0,0 +1,42 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts/GBTest.cs", + "EntryPointFileDirectoryPath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/GeoBlazorBuild b/build-tools/osx-arm64/GeoBlazorBuild new file mode 100644 index 000000000..5222c71a8 Binary files /dev/null and b/build-tools/osx-arm64/GeoBlazorBuild differ diff --git a/build-tools/osx-arm64/GeoBlazorBuild.deps.json b/build-tools/osx-arm64/GeoBlazorBuild.deps.json new file mode 100644 index 000000000..8fd316b71 --- /dev/null +++ b/build-tools/osx-arm64/GeoBlazorBuild.deps.json @@ -0,0 +1,56 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/osx-arm64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/osx-arm64": { + "GeoBlazorBuild/1.0.0": { + "dependencies": { + "Polly.Core": "8.6.5", + "Utilities": "1.0.0" + }, + "runtime": { + "GeoBlazorBuild.dll": {} + } + }, + "Polly.Core/8.6.5": { + "runtime": { + "lib/net8.0/Polly.Core.dll": { + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.6.5.5194" + } + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "GeoBlazorBuild/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Polly.Core/8.6.5": { + "type": "package", + "serviceable": true, + "sha512": "sha512-t+sUVrIwvo7UmsgHGgOG9F0GDZSRIm47u2ylH17Gvcv1q5hNEwgD5GoBlFyc0kh/pebmPyrAgvGsR/65ZBaXlg==", + "path": "polly.core/8.6.5", + "hashPath": "polly.core.8.6.5.nupkg.sha512" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/GeoBlazorBuild.dll b/build-tools/osx-arm64/GeoBlazorBuild.dll new file mode 100644 index 000000000..8261af6b0 Binary files /dev/null and b/build-tools/osx-arm64/GeoBlazorBuild.dll differ diff --git a/build-tools/GeoBlazorBuild.runtimeconfig.json b/build-tools/osx-arm64/GeoBlazorBuild.runtimeconfig.json similarity index 82% rename from build-tools/GeoBlazorBuild.runtimeconfig.json rename to build-tools/osx-arm64/GeoBlazorBuild.runtimeconfig.json index 789dbb8d2..8bcc0866b 100644 --- a/build-tools/GeoBlazorBuild.runtimeconfig.json +++ b/build-tools/osx-arm64/GeoBlazorBuild.runtimeconfig.json @@ -5,9 +5,15 @@ "name": "Microsoft.NETCore.App", "version": "10.0.0" }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, "configProperties": { - "EntryPointFilePath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts/GeoBlazorBuild.cs", - "EntryPointFileDirectoryPath": "/home/runner/work/GeoBlazor/GeoBlazor/build-scripts", + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\GeoBlazorBuild.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, "System.ComponentModel.DefaultValueAttribute.IsSupported": false, "System.ComponentModel.Design.IDesignerHost.IsSupported": false, diff --git a/build-tools/osx-arm64/Polly.Core.dll b/build-tools/osx-arm64/Polly.Core.dll new file mode 100644 index 000000000..57eae2055 Binary files /dev/null and b/build-tools/osx-arm64/Polly.Core.dll differ diff --git a/build-tools/osx-arm64/RazorCopy b/build-tools/osx-arm64/RazorCopy new file mode 100755 index 000000000..1e127bbcf Binary files /dev/null and b/build-tools/osx-arm64/RazorCopy differ diff --git a/build-tools/osx-arm64/RazorCopy.deps.json b/build-tools/osx-arm64/RazorCopy.deps.json new file mode 100644 index 000000000..8f701176f --- /dev/null +++ b/build-tools/osx-arm64/RazorCopy.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/osx-arm64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/osx-arm64": { + "RazorCopy/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "RazorCopy.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "RazorCopy/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/osx-arm64/RazorCopy.dll b/build-tools/osx-arm64/RazorCopy.dll new file mode 100644 index 000000000..5c6af7818 Binary files /dev/null and b/build-tools/osx-arm64/RazorCopy.dll differ diff --git a/build-tools/osx-arm64/RazorCopy.runtimeconfig.json b/build-tools/osx-arm64/RazorCopy.runtimeconfig.json new file mode 100644 index 000000000..9110f93bb --- /dev/null +++ b/build-tools/osx-arm64/RazorCopy.runtimeconfig.json @@ -0,0 +1,42 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts/RazorCopy.cs", + "EntryPointFileDirectoryPath": "/Users/timpurdum/repos/GeoBlazor/GeoBlazor.Pro/GeoBlazor/build-tools/build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/ConsoleDialog.deps.json b/build-tools/osx-arm64/Utilities.deps.json similarity index 77% rename from build-tools/ConsoleDialog.deps.json rename to build-tools/osx-arm64/Utilities.deps.json index 6a5d3eb14..88bf821d5 100644 --- a/build-tools/ConsoleDialog.deps.json +++ b/build-tools/osx-arm64/Utilities.deps.json @@ -6,15 +6,15 @@ "compilationOptions": {}, "targets": { ".NETCoreApp,Version=v10.0": { - "ConsoleDialog/1.0.0": { + "Utilities/1.0.0": { "runtime": { - "ConsoleDialog.dll": {} + "Utilities.dll": {} } } } }, "libraries": { - "ConsoleDialog/1.0.0": { + "Utilities/1.0.0": { "type": "project", "serviceable": false, "sha512": "" diff --git a/build-tools/osx-arm64/Utilities.dll b/build-tools/osx-arm64/Utilities.dll new file mode 100644 index 000000000..0e8b91703 Binary files /dev/null and b/build-tools/osx-arm64/Utilities.dll differ diff --git a/build-tools/utilities/PathFinder.cs b/build-tools/utilities/PathFinder.cs new file mode 100644 index 000000000..2786b5052 --- /dev/null +++ b/build-tools/utilities/PathFinder.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; + +namespace Utilities; + +public static class PathFinder +{ + /// + /// Gets the relative directory containing the build scripts. + /// + public static string GetScriptsDirectory([CallerFilePath] string? callerFilePath = null) + { + string dllDirectory = AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + if (dllDirectory.Contains("dotnet")) + { + // we are running from the C# script in build-scripts, so we can use the caller file path to find the script directory + return Path.GetDirectoryName(callerFilePath!)!; + } + + // otherwise the dll is stored in ./build-tools/{os}-{arch} + return Path.GetFullPath(Path.Combine(dllDirectory, "..", "build-scripts")); + } +} \ No newline at end of file diff --git a/build-tools/utilities/Utilities.csproj b/build-tools/utilities/Utilities.csproj new file mode 100644 index 000000000..0a280f4b6 --- /dev/null +++ b/build-tools/utilities/Utilities.csproj @@ -0,0 +1,10 @@ + + + + Library + net10.0 + enable + enable + + + diff --git a/build-tools/BuildAppSettings.deps.json b/build-tools/win-x64/BuildAppSettings.deps.json similarity index 72% rename from build-tools/BuildAppSettings.deps.json rename to build-tools/win-x64/BuildAppSettings.deps.json index 741cd3fa3..4408e5b0d 100644 --- a/build-tools/BuildAppSettings.deps.json +++ b/build-tools/win-x64/BuildAppSettings.deps.json @@ -1,11 +1,12 @@ { "runtimeTarget": { - "name": ".NETCoreApp,Version=v10.0", + "name": ".NETCoreApp,Version=v10.0/win-x64", "signature": "" }, "compilationOptions": {}, "targets": { - ".NETCoreApp,Version=v10.0": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/win-x64": { "BuildAppSettings/1.0.0": { "runtime": { "BuildAppSettings.dll": {} diff --git a/build-tools/win-x64/BuildAppSettings.dll b/build-tools/win-x64/BuildAppSettings.dll new file mode 100644 index 000000000..3760edb14 Binary files /dev/null and b/build-tools/win-x64/BuildAppSettings.dll differ diff --git a/build-tools/BuildAppSettings.exe b/build-tools/win-x64/BuildAppSettings.exe similarity index 99% rename from build-tools/BuildAppSettings.exe rename to build-tools/win-x64/BuildAppSettings.exe index fb1f90b5e..7926f9cf3 100644 Binary files a/build-tools/BuildAppSettings.exe and b/build-tools/win-x64/BuildAppSettings.exe differ diff --git a/build-tools/win-x64/BuildAppSettings.runtimeconfig.json b/build-tools/win-x64/BuildAppSettings.runtimeconfig.json new file mode 100644 index 000000000..73d6bb616 --- /dev/null +++ b/build-tools/win-x64/BuildAppSettings.runtimeconfig.json @@ -0,0 +1,43 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\BuildAppSettings.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Threading.ThreadPool.UseWindowsThreadPool": true, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/BuildTemplates.deps.json b/build-tools/win-x64/BuildTemplates.deps.json new file mode 100644 index 000000000..4e16a2b90 --- /dev/null +++ b/build-tools/win-x64/BuildTemplates.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/win-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/win-x64": { + "BuildTemplates/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "BuildTemplates.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "BuildTemplates/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/BuildTemplates.dll b/build-tools/win-x64/BuildTemplates.dll new file mode 100644 index 000000000..c6df2af7c Binary files /dev/null and b/build-tools/win-x64/BuildTemplates.dll differ diff --git a/build-tools/BuildTemplates.exe b/build-tools/win-x64/BuildTemplates.exe similarity index 99% rename from build-tools/BuildTemplates.exe rename to build-tools/win-x64/BuildTemplates.exe index dd104b61f..576e29f29 100644 Binary files a/build-tools/BuildTemplates.exe and b/build-tools/win-x64/BuildTemplates.exe differ diff --git a/build-tools/win-x64/BuildTemplates.runtimeconfig.json b/build-tools/win-x64/BuildTemplates.runtimeconfig.json new file mode 100644 index 000000000..7571144b5 --- /dev/null +++ b/build-tools/win-x64/BuildTemplates.runtimeconfig.json @@ -0,0 +1,21 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\BuildTemplates.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/CliWrap.dll b/build-tools/win-x64/CliWrap.dll new file mode 100644 index 000000000..3996990be Binary files /dev/null and b/build-tools/win-x64/CliWrap.dll differ diff --git a/build-tools/win-x64/ConsoleDialog.deps.json b/build-tools/win-x64/ConsoleDialog.deps.json new file mode 100644 index 000000000..a72258128 --- /dev/null +++ b/build-tools/win-x64/ConsoleDialog.deps.json @@ -0,0 +1,24 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/win-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/win-x64": { + "ConsoleDialog/1.0.0": { + "runtime": { + "ConsoleDialog.dll": {} + } + } + } + }, + "libraries": { + "ConsoleDialog/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/ConsoleDialog.dll b/build-tools/win-x64/ConsoleDialog.dll new file mode 100644 index 000000000..ce481e47a Binary files /dev/null and b/build-tools/win-x64/ConsoleDialog.dll differ diff --git a/build-tools/ConsoleDialog.exe b/build-tools/win-x64/ConsoleDialog.exe similarity index 99% rename from build-tools/ConsoleDialog.exe rename to build-tools/win-x64/ConsoleDialog.exe index 67de680ee..a703fdeb2 100644 Binary files a/build-tools/ConsoleDialog.exe and b/build-tools/win-x64/ConsoleDialog.exe differ diff --git a/build-tools/win-x64/ConsoleDialog.runtimeconfig.json b/build-tools/win-x64/ConsoleDialog.runtimeconfig.json new file mode 100644 index 000000000..10a4d2205 --- /dev/null +++ b/build-tools/win-x64/ConsoleDialog.runtimeconfig.json @@ -0,0 +1,43 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\ConsoleDialog.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Threading.ThreadPool.UseWindowsThreadPool": true, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/ESBuild.deps.json b/build-tools/win-x64/ESBuild.deps.json new file mode 100644 index 000000000..21ef4726f --- /dev/null +++ b/build-tools/win-x64/ESBuild.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/win-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/win-x64": { + "ESBuild/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "ESBuild.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "ESBuild/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/ESBuild.dll b/build-tools/win-x64/ESBuild.dll new file mode 100644 index 000000000..293bb1f85 Binary files /dev/null and b/build-tools/win-x64/ESBuild.dll differ diff --git a/build-tools/ESBuild.exe b/build-tools/win-x64/ESBuild.exe similarity index 99% rename from build-tools/ESBuild.exe rename to build-tools/win-x64/ESBuild.exe index ca0e02ca4..d07e40126 100644 Binary files a/build-tools/ESBuild.exe and b/build-tools/win-x64/ESBuild.exe differ diff --git a/build-tools/win-x64/ESBuild.runtimeconfig.json b/build-tools/win-x64/ESBuild.runtimeconfig.json new file mode 100644 index 000000000..ad7ac7744 --- /dev/null +++ b/build-tools/win-x64/ESBuild.runtimeconfig.json @@ -0,0 +1,43 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\ESBuild.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Threading.ThreadPool.UseWindowsThreadPool": true, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/ESBuildClearLocks.deps.json b/build-tools/win-x64/ESBuildClearLocks.deps.json new file mode 100644 index 000000000..8fa67b657 --- /dev/null +++ b/build-tools/win-x64/ESBuildClearLocks.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/win-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/win-x64": { + "ESBuildClearLocks/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "ESBuildClearLocks.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "ESBuildClearLocks/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/ESBuildClearLocks.dll b/build-tools/win-x64/ESBuildClearLocks.dll new file mode 100644 index 000000000..6268cf9b8 Binary files /dev/null and b/build-tools/win-x64/ESBuildClearLocks.dll differ diff --git a/build-tools/ESBuildClearLocks.exe b/build-tools/win-x64/ESBuildClearLocks.exe similarity index 99% rename from build-tools/ESBuildClearLocks.exe rename to build-tools/win-x64/ESBuildClearLocks.exe index 14ba34676..0de92f9bc 100644 Binary files a/build-tools/ESBuildClearLocks.exe and b/build-tools/win-x64/ESBuildClearLocks.exe differ diff --git a/build-tools/win-x64/ESBuildClearLocks.runtimeconfig.json b/build-tools/win-x64/ESBuildClearLocks.runtimeconfig.json new file mode 100644 index 000000000..e2f7b35df --- /dev/null +++ b/build-tools/win-x64/ESBuildClearLocks.runtimeconfig.json @@ -0,0 +1,43 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\ESBuildClearLocks.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Threading.ThreadPool.UseWindowsThreadPool": true, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/FetchNuGetVersion.deps.json b/build-tools/win-x64/FetchNuGetVersion.deps.json similarity index 72% rename from build-tools/FetchNuGetVersion.deps.json rename to build-tools/win-x64/FetchNuGetVersion.deps.json index 079217a0b..6a7a53c41 100644 --- a/build-tools/FetchNuGetVersion.deps.json +++ b/build-tools/win-x64/FetchNuGetVersion.deps.json @@ -1,11 +1,12 @@ { "runtimeTarget": { - "name": ".NETCoreApp,Version=v10.0", + "name": ".NETCoreApp,Version=v10.0/win-x64", "signature": "" }, "compilationOptions": {}, "targets": { - ".NETCoreApp,Version=v10.0": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/win-x64": { "FetchNuGetVersion/1.0.0": { "runtime": { "FetchNuGetVersion.dll": {} diff --git a/build-tools/win-x64/FetchNuGetVersion.dll b/build-tools/win-x64/FetchNuGetVersion.dll new file mode 100644 index 000000000..bfeb7ac42 Binary files /dev/null and b/build-tools/win-x64/FetchNuGetVersion.dll differ diff --git a/build-tools/FetchNuGetVersion.exe b/build-tools/win-x64/FetchNuGetVersion.exe similarity index 99% rename from build-tools/FetchNuGetVersion.exe rename to build-tools/win-x64/FetchNuGetVersion.exe index f58d02133..22f45a5e2 100644 Binary files a/build-tools/FetchNuGetVersion.exe and b/build-tools/win-x64/FetchNuGetVersion.exe differ diff --git a/build-tools/win-x64/FetchNuGetVersion.runtimeconfig.json b/build-tools/win-x64/FetchNuGetVersion.runtimeconfig.json new file mode 100644 index 000000000..cfc1e6e72 --- /dev/null +++ b/build-tools/win-x64/FetchNuGetVersion.runtimeconfig.json @@ -0,0 +1,43 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\FetchNuGetVersion.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Threading.ThreadPool.UseWindowsThreadPool": true, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/GBTest.deps.json b/build-tools/win-x64/GBTest.deps.json new file mode 100644 index 000000000..c4cfd271b --- /dev/null +++ b/build-tools/win-x64/GBTest.deps.json @@ -0,0 +1,56 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/win-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/win-x64": { + "GBTest/1.0.0": { + "dependencies": { + "CliWrap": "3.10.0", + "Utilities": "1.0.0" + }, + "runtime": { + "GBTest.dll": {} + } + }, + "CliWrap/3.10.0": { + "runtime": { + "lib/net10.0/CliWrap.dll": { + "assemblyVersion": "3.10.0.0", + "fileVersion": "3.10.0.0" + } + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "GBTest/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "CliWrap/3.10.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Igph39WNImUFqQrpTeBDPafcCnM1u/8IooZHqMRF/5JdV5H78p+AbZkeYY1Nkz1WlbI/Gt2Hq/3rYh55jxRF9Q==", + "path": "cliwrap/3.10.0", + "hashPath": "cliwrap.3.10.0.nupkg.sha512" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/GBTest.dll b/build-tools/win-x64/GBTest.dll new file mode 100644 index 000000000..50880d4d1 Binary files /dev/null and b/build-tools/win-x64/GBTest.dll differ diff --git a/build-tools/ESBuildWaitForCompletion.exe b/build-tools/win-x64/GBTest.exe similarity index 99% rename from build-tools/ESBuildWaitForCompletion.exe rename to build-tools/win-x64/GBTest.exe index e707c8c89..bf98d19e3 100644 Binary files a/build-tools/ESBuildWaitForCompletion.exe and b/build-tools/win-x64/GBTest.exe differ diff --git a/build-tools/win-x64/GBTest.runtimeconfig.json b/build-tools/win-x64/GBTest.runtimeconfig.json new file mode 100644 index 000000000..0feb20dac --- /dev/null +++ b/build-tools/win-x64/GBTest.runtimeconfig.json @@ -0,0 +1,43 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\GBTest.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Threading.ThreadPool.UseWindowsThreadPool": true, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/GeoBlazorBuild.deps.json b/build-tools/win-x64/GeoBlazorBuild.deps.json new file mode 100644 index 000000000..113b19007 --- /dev/null +++ b/build-tools/win-x64/GeoBlazorBuild.deps.json @@ -0,0 +1,56 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/win-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/win-x64": { + "GeoBlazorBuild/1.0.0": { + "dependencies": { + "Polly.Core": "8.6.5", + "Utilities": "1.0.0" + }, + "runtime": { + "GeoBlazorBuild.dll": {} + } + }, + "Polly.Core/8.6.5": { + "runtime": { + "lib/net8.0/Polly.Core.dll": { + "assemblyVersion": "8.0.0.0", + "fileVersion": "8.6.5.5194" + } + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "GeoBlazorBuild/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Polly.Core/8.6.5": { + "type": "package", + "serviceable": true, + "sha512": "sha512-t+sUVrIwvo7UmsgHGgOG9F0GDZSRIm47u2ylH17Gvcv1q5hNEwgD5GoBlFyc0kh/pebmPyrAgvGsR/65ZBaXlg==", + "path": "polly.core/8.6.5", + "hashPath": "polly.core.8.6.5.nupkg.sha512" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/GeoBlazorBuild.dll b/build-tools/win-x64/GeoBlazorBuild.dll new file mode 100644 index 000000000..6ef61d186 Binary files /dev/null and b/build-tools/win-x64/GeoBlazorBuild.dll differ diff --git a/build-tools/GeoBlazorBuild.exe b/build-tools/win-x64/GeoBlazorBuild.exe similarity index 99% rename from build-tools/GeoBlazorBuild.exe rename to build-tools/win-x64/GeoBlazorBuild.exe index a41d208b2..1b06460aa 100644 Binary files a/build-tools/GeoBlazorBuild.exe and b/build-tools/win-x64/GeoBlazorBuild.exe differ diff --git a/build-tools/win-x64/GeoBlazorBuild.runtimeconfig.json b/build-tools/win-x64/GeoBlazorBuild.runtimeconfig.json new file mode 100644 index 000000000..9fdcf5341 --- /dev/null +++ b/build-tools/win-x64/GeoBlazorBuild.runtimeconfig.json @@ -0,0 +1,43 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\GeoBlazorBuild.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Threading.ThreadPool.UseWindowsThreadPool": true, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/Polly.Core.dll b/build-tools/win-x64/Polly.Core.dll new file mode 100644 index 000000000..57eae2055 Binary files /dev/null and b/build-tools/win-x64/Polly.Core.dll differ diff --git a/build-tools/win-x64/RazorCopy.deps.json b/build-tools/win-x64/RazorCopy.deps.json new file mode 100644 index 000000000..52cf77052 --- /dev/null +++ b/build-tools/win-x64/RazorCopy.deps.json @@ -0,0 +1,40 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v10.0/win-x64", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v10.0": {}, + ".NETCoreApp,Version=v10.0/win-x64": { + "RazorCopy/1.0.0": { + "dependencies": { + "Utilities": "1.0.0" + }, + "runtime": { + "RazorCopy.dll": {} + } + }, + "Utilities/1.0.0": { + "runtime": { + "Utilities.dll": { + "assemblyVersion": "1.0.0.0", + "fileVersion": "1.0.0.0" + } + } + } + } + }, + "libraries": { + "RazorCopy/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Utilities/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/build-tools/win-x64/RazorCopy.dll b/build-tools/win-x64/RazorCopy.dll new file mode 100644 index 000000000..5b2f448b2 Binary files /dev/null and b/build-tools/win-x64/RazorCopy.dll differ diff --git a/build-tools/win-x64/RazorCopy.exe b/build-tools/win-x64/RazorCopy.exe new file mode 100644 index 000000000..5068ec195 Binary files /dev/null and b/build-tools/win-x64/RazorCopy.exe differ diff --git a/build-tools/win-x64/RazorCopy.runtimeconfig.json b/build-tools/win-x64/RazorCopy.runtimeconfig.json new file mode 100644 index 000000000..22ec0e58a --- /dev/null +++ b/build-tools/win-x64/RazorCopy.runtimeconfig.json @@ -0,0 +1,43 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\RazorCopy.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Threading.ThreadPool.UseWindowsThreadPool": true, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/GeoBlazorBuild.deps.json b/build-tools/win-x64/ScriptBuilder.deps.json similarity index 76% rename from build-tools/GeoBlazorBuild.deps.json rename to build-tools/win-x64/ScriptBuilder.deps.json index d70590fb0..0453f7f51 100644 --- a/build-tools/GeoBlazorBuild.deps.json +++ b/build-tools/win-x64/ScriptBuilder.deps.json @@ -6,15 +6,15 @@ "compilationOptions": {}, "targets": { ".NETCoreApp,Version=v10.0": { - "GeoBlazorBuild/1.0.0": { + "ScriptBuilder/1.0.0": { "runtime": { - "GeoBlazorBuild.dll": {} + "ScriptBuilder.dll": {} } } } }, "libraries": { - "GeoBlazorBuild/1.0.0": { + "ScriptBuilder/1.0.0": { "type": "project", "serviceable": false, "sha512": "" diff --git a/build-tools/win-x64/ScriptBuilder.dll b/build-tools/win-x64/ScriptBuilder.dll new file mode 100644 index 000000000..90cda6725 Binary files /dev/null and b/build-tools/win-x64/ScriptBuilder.dll differ diff --git a/build-tools/win-x64/ScriptBuilder.exe b/build-tools/win-x64/ScriptBuilder.exe new file mode 100644 index 000000000..6f3eaa6ed Binary files /dev/null and b/build-tools/win-x64/ScriptBuilder.exe differ diff --git a/build-tools/win-x64/ScriptBuilder.runtimeconfig.json b/build-tools/win-x64/ScriptBuilder.runtimeconfig.json new file mode 100644 index 000000000..bf9935c2d --- /dev/null +++ b/build-tools/win-x64/ScriptBuilder.runtimeconfig.json @@ -0,0 +1,42 @@ +{ + "runtimeOptions": { + "tfm": "net10.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "10.0.0" + }, + "runtimeOptions": { + "configProperties": { + "EntryPointFilePath": ".\\GBTest.cs", + "EntryPointFileDirectoryPath": "." + } + }, + "configProperties": { + "EntryPointFilePath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts\\ScriptBuilder.cs", + "EntryPointFileDirectoryPath": "D:\\dymaptic.GeoBlazor.CodeGen\\GeoBlazor.Pro\\GeoBlazor\\build-tools\\build-scripts", + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} \ No newline at end of file diff --git a/build-tools/BuildTemplates.deps.json b/build-tools/win-x64/Utilities.deps.json similarity index 76% rename from build-tools/BuildTemplates.deps.json rename to build-tools/win-x64/Utilities.deps.json index 3e618ad81..88bf821d5 100644 --- a/build-tools/BuildTemplates.deps.json +++ b/build-tools/win-x64/Utilities.deps.json @@ -6,15 +6,15 @@ "compilationOptions": {}, "targets": { ".NETCoreApp,Version=v10.0": { - "BuildTemplates/1.0.0": { + "Utilities/1.0.0": { "runtime": { - "BuildTemplates.dll": {} + "Utilities.dll": {} } } } }, "libraries": { - "BuildTemplates/1.0.0": { + "Utilities/1.0.0": { "type": "project", "serviceable": false, "sha512": "" diff --git a/build-tools/win-x64/Utilities.dll b/build-tools/win-x64/Utilities.dll new file mode 100644 index 000000000..de82532aa Binary files /dev/null and b/build-tools/win-x64/Utilities.dll differ diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index d890f9ec7..8f69a16cf 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -3,12 +3,14 @@ This document is intended for developers and maintainers of the GeoBlazor source development and refactoring of existing code, but may not be adhered to by all existing code. ## TypeScript/ESBuild -The TypeScript files are compiled as part of the build process using a source generator and ESBuild. -The source generator, `ESBuildLauncher.cs`, watches the `Scripts` folder for changes, and then runs the ESBuild + +The TypeScript files are compiled as part of the build process using a special `NoTargets` SDK project that fires off a +script, `ESBuild.cs`. +The script watches the `dymaptic.GeoBlazor.Core/Scripts` folder for changes, and then runs the ESBuild compiler on the TypeScript files. The output is placed in the `wwwroot/js` folder. -If you are making changes to the TypeScript files, you can run the ESBuild compiler manually by running -`src/dymaptic.GeoBlazor.Core/esBuild.ps1`. You do not necessarily need to restart the .NET application +If you are making changes to the TypeScript files, you can run the ESBuild compiler manually by running +`dotnet build-tools/build-scripts/ESBuild.cs`. You do not necessarily need to restart the .NET application for every change, unless you see an issue after refreshing the browser with the cache disabled. ## **Build Errors!!!** diff --git a/src/dymaptic.GeoBlazor.Core.sln.DotSettings b/dymaptic.GeoBlazor.Core.sln.DotSettings similarity index 100% rename from src/dymaptic.GeoBlazor.Core.sln.DotSettings rename to dymaptic.GeoBlazor.Core.sln.DotSettings diff --git a/dymaptic.GeoBlazor.Core.slnx b/dymaptic.GeoBlazor.Core.slnx new file mode 100644 index 000000000..53248412d --- /dev/null +++ b/dymaptic.GeoBlazor.Core.slnx @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.Maui/dymaptic.GeoBlazor.Core.Sample.Maui.csproj b/samples/dymaptic.GeoBlazor.Core.Sample.Maui/dymaptic.GeoBlazor.Core.Sample.Maui.csproj index c2a930d9a..ce4b75e3b 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.Maui/dymaptic.GeoBlazor.Core.Sample.Maui.csproj +++ b/samples/dymaptic.GeoBlazor.Core.Sample.Maui/dymaptic.GeoBlazor.Core.Sample.Maui.csproj @@ -1,8 +1,9 @@  - net10.0-android;net10.0-ios;net10.0-maccatalyst - $(TargetFrameworks);net10.0-windows10.0.19041.0 + net10.0-android + $(TargetFrameworks);net10.0-windows10.0.19041.0 + $(TargetFrameworks);net10.0-ios;net10.0-maccatalyst Exe dymaptic.GeoBlazor.Core.Sample.Maui true @@ -52,11 +53,11 @@ - - - - - + + + + + diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth.Client/dymaptic.GeoBlazor.Core.Sample.OAuth.Client.csproj b/samples/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth.Client/dymaptic.GeoBlazor.Core.Sample.OAuth.Client.csproj index 212ce0fb7..8d1376c8b 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth.Client/dymaptic.GeoBlazor.Core.Sample.OAuth.Client.csproj +++ b/samples/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth.Client/dymaptic.GeoBlazor.Core.Sample.OAuth.Client.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth.csproj b/samples/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth.csproj index e99eb002d..bbc1cb3f3 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth.csproj +++ b/samples/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth/dymaptic.GeoBlazor.Core.Sample.OAuth.csproj @@ -8,7 +8,7 @@ - + diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/UniqueValueRenderers.razor b/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/UniqueValueRenderers.razor index 78099485b..324aeb703 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/UniqueValueRenderers.razor +++ b/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/UniqueValueRenderers.razor @@ -3,44 +3,24 @@

Unique Value Renderers

- A US Freeway map rendered with a custom Unique Value Renderer that color-codes different highway types. + A US Highway map rendered with a custom Unique Value Renderer that color-codes different road types.

- + - - - - - - - - - - - - - - - - - - + Renderer="_uniqueValueRenderer"> + + @if (_showLegend) @@ -48,8 +28,4 @@ } - - -@code { - private bool _showLegend; -} \ No newline at end of file + \ No newline at end of file diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/UniqueValueRenderers.razor.cs b/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/UniqueValueRenderers.razor.cs new file mode 100644 index 000000000..a21a830dc --- /dev/null +++ b/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/UniqueValueRenderers.razor.cs @@ -0,0 +1,68 @@ +using dymaptic.GeoBlazor.Core.Components; +using dymaptic.GeoBlazor.Core.Components.Renderers; +using dymaptic.GeoBlazor.Core.Components.Symbols; +using dymaptic.GeoBlazor.Core.Enums; +using dymaptic.GeoBlazor.Core.Extensions; +using dymaptic.GeoBlazor.Core.Model; + + +namespace dymaptic.GeoBlazor.Core.Sample.Shared.Pages; + +public partial class UniqueValueRenderers +{ + private static readonly Dictionary roadTypes = new() + { + // Major highways - wide, bold colors + ["motorway"] = new SimpleLineSymbol(new MapColor(232, 63, 111), 5, SimpleLineSymbolStyle.Solid), + ["motorway_link"] = new SimpleLineSymbol(new MapColor(232, 63, 111), 3, SimpleLineSymbolStyle.Solid), + ["trunk"] = new SimpleLineSymbol(new MapColor(247, 148, 29), 4, SimpleLineSymbolStyle.Solid), + ["trunk_link"] = new SimpleLineSymbol(new MapColor(247, 148, 29), 2, SimpleLineSymbolStyle.Solid), + + // Primary roads - medium-wide, warm colors + ["primary"] = new SimpleLineSymbol(new MapColor(255, 200, 69), 4, SimpleLineSymbolStyle.Solid), + ["primary_link"] = new SimpleLineSymbol(new MapColor(255, 200, 69), 2, SimpleLineSymbolStyle.Solid), + + // Secondary roads - medium width, cooler tones + ["secondary"] = new SimpleLineSymbol(new MapColor(141, 198, 63), 3, SimpleLineSymbolStyle.Solid), + ["secondary_link"] = new SimpleLineSymbol(new MapColor(141, 198, 63), 2, SimpleLineSymbolStyle.Solid), + + // Tertiary roads - narrower + ["tertiary"] = new SimpleLineSymbol(new MapColor(102, 178, 255), 2.5, SimpleLineSymbolStyle.Solid), + ["tertiary_link"] = new SimpleLineSymbol(new MapColor(102, 178, 255), 1.5, SimpleLineSymbolStyle.Solid), + + // Local roads - thin, neutral colors + ["residential"] = new SimpleLineSymbol(new MapColor(200, 200, 200), 2, SimpleLineSymbolStyle.Solid), + ["living_street"] = new SimpleLineSymbol(new MapColor(180, 180, 220), 2, SimpleLineSymbolStyle.Solid), + ["unclassified"] = new SimpleLineSymbol(new MapColor(170, 170, 170), 1.5, SimpleLineSymbolStyle.Solid), + ["road"] = new SimpleLineSymbol(new MapColor(150, 150, 150), 1.5, SimpleLineSymbolStyle.Solid), + + // Service and access roads + ["service"] = new SimpleLineSymbol(new MapColor(128, 128, 128), 1, SimpleLineSymbolStyle.Solid), + ["services"] = new SimpleLineSymbol(new MapColor(128, 128, 128), 1, SimpleLineSymbolStyle.Solid), + + // Pedestrian paths - thin, dashed styles, earth tones + ["footway"] = new SimpleLineSymbol(new MapColor(139, 90, 43), 1, SimpleLineSymbolStyle.Dash), + ["path"] = new SimpleLineSymbol(new MapColor(160, 82, 45), 1, SimpleLineSymbolStyle.Dash), + ["pedestrian"] = new SimpleLineSymbol(new MapColor(139, 69, 19), 1.5, SimpleLineSymbolStyle.ShortDash), + ["steps"] = new SimpleLineSymbol(new MapColor(105, 105, 105), 1, SimpleLineSymbolStyle.Dot), + ["corridor"] = new SimpleLineSymbol(new MapColor(169, 169, 169), 1, SimpleLineSymbolStyle.ShortDash), + + // Off-road and rural paths + ["track"] = new SimpleLineSymbol(new MapColor(139, 119, 101), 1.5, SimpleLineSymbolStyle.LongDash), + ["bridleway"] = new SimpleLineSymbol(new MapColor(107, 142, 35), 1, SimpleLineSymbolStyle.DashDot), + + // Special purpose roads + ["busway"] = new SimpleLineSymbol(new MapColor(0, 112, 192), 2.5, SimpleLineSymbolStyle.Solid), + ["raceway"] = new SimpleLineSymbol(new MapColor(255, 0, 0), 3, SimpleLineSymbolStyle.Solid), + + // Under development - distinctive dashed patterns + ["construction"] = new SimpleLineSymbol(new MapColor(255, 165, 0), 2, SimpleLineSymbolStyle.DashDot), + ["proposed"] = new SimpleLineSymbol(new MapColor(192, 192, 192), 1.5, SimpleLineSymbolStyle.Dot) + }; + private readonly UniqueValueRenderer _uniqueValueRenderer = new(uniqueValueInfos: roadTypes + .Select(r => new UniqueValueInfo(r.Key.ToUpperFirstChar().Replace("_", " "), r.Value, r.Key)) + .ToArray(), + field: "highway", defaultLabel: "Service", + legendOptions: new UniqueValueRendererLegendOptions("Route Type")); + private bool _showLegend; +} \ No newline at end of file diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WCSLayers.razor b/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WCSLayers.razor index 19d338b75..ae29f32cf 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WCSLayers.razor +++ b/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WCSLayers.razor @@ -55,29 +55,44 @@ private void OnViewRendered() => _mapRendered = true; - public async Task AddRemoveWCSLayer(bool colorized) + private async Task AddRemoveWCSLayer(bool colorized) { - if (!_view!.Map!.Layers.Any()) + await SetCursor("wait"); + await Task.Delay(100); + + try { - if (colorized) + if (!_view!.Map!.Layers.Any()) + { + if (colorized) + { + await _view.AddLayer(_colorizedLayer!); + } + else + { + await _view.AddLayer(_blackAndWhiteLayer!); + } + } + else if (colorized) { - await _view.AddLayer(_colorizedLayer!); + await _view.RemoveLayer(_colorizedLayer!); } else { - await _view.AddLayer(_blackAndWhiteLayer!); + await _view.RemoveLayer(_blackAndWhiteLayer!); } } - else if (colorized) - { - await _view.RemoveLayer(_colorizedLayer!); - } - else + finally { - await _view.RemoveLayer(_blackAndWhiteLayer!); + await SetCursor("default"); } } + private async Task SetCursor(string cursor) + { + await _view!.CoreJsModule!.InvokeVoidAsync("setCursor", cursor, _view.Id); + } + private MapView? _view; private bool _mapRendered; diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WMSLayers.razor b/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WMSLayers.razor index feda6d1a5..38f3242a9 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WMSLayers.razor +++ b/samples/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WMSLayers.razor @@ -21,12 +21,12 @@ This sample shows how to access a WMS Service using WMSLayer and add it to a scene as a Basemap. The WMSLayer is used to create layers based on OGC Web Map Services (WMS).

- + - + diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.Shared/dymaptic.GeoBlazor.Core.Sample.Shared.csproj b/samples/dymaptic.GeoBlazor.Core.Sample.Shared/dymaptic.GeoBlazor.Core.Sample.Shared.csproj index 83447a3cc..79c1b6a25 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.Shared/dymaptic.GeoBlazor.Core.Sample.Shared.csproj +++ b/samples/dymaptic.GeoBlazor.Core.Sample.Shared/dymaptic.GeoBlazor.Core.Sample.Shared.csproj @@ -12,9 +12,9 @@ - - - + + + diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.Client/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.Client.csproj b/samples/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.Client/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.Client.csproj index 81c1ffe44..92b619fa4 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.Client/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.Client.csproj +++ b/samples/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.Client/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.Client.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.csproj b/samples/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.csproj index 07dfbd614..a44cc4a65 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.csproj +++ b/samples/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh/dymaptic.GeoBlazor.Core.Sample.TokenRefresh.csproj @@ -8,7 +8,7 @@ - + diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.Wasm/dymaptic.GeoBlazor.Core.Sample.Wasm.csproj b/samples/dymaptic.GeoBlazor.Core.Sample.Wasm/dymaptic.GeoBlazor.Core.Sample.Wasm.csproj index 60b8c4c69..565eec732 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.Wasm/dymaptic.GeoBlazor.Core.Sample.Wasm.csproj +++ b/samples/dymaptic.GeoBlazor.Core.Sample.Wasm/dymaptic.GeoBlazor.Core.Sample.Wasm.csproj @@ -5,8 +5,8 @@ - - + + diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp.Client/dymaptic.GeoBlazor.Core.Sample.WebApp.Client.csproj b/samples/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp.Client/dymaptic.GeoBlazor.Core.Sample.WebApp.Client.csproj index 5fb04f61b..7a531c895 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp.Client/dymaptic.GeoBlazor.Core.Sample.WebApp.Client.csproj +++ b/samples/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp.Client/dymaptic.GeoBlazor.Core.Sample.WebApp.Client.csproj @@ -7,7 +7,7 @@ - + diff --git a/samples/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp.csproj b/samples/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp.csproj index e1ac12030..deb1536e3 100644 --- a/samples/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp.csproj +++ b/samples/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp/dymaptic.GeoBlazor.Core.Sample.WebApp.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/dymaptic.GeoBlazor.Core.Analyzers/dymaptic.GeoBlazor.Core.Analyzers.csproj b/src/dymaptic.GeoBlazor.Core.Analyzers/dymaptic.GeoBlazor.Core.Analyzers.csproj index 490621065..6ec3becf8 100644 --- a/src/dymaptic.GeoBlazor.Core.Analyzers/dymaptic.GeoBlazor.Core.Analyzers.csproj +++ b/src/dymaptic.GeoBlazor.Core.Analyzers/dymaptic.GeoBlazor.Core.Analyzers.csproj @@ -3,9 +3,7 @@ netstandard2.0 false - enable latest - true true dymaptic.GeoBlazor.Core.Analyzers @@ -16,10 +14,11 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + + diff --git a/src/dymaptic.GeoBlazor.Core.ESBuild/dymaptic.GeoBlazor.Core.ESBuild.csproj b/src/dymaptic.GeoBlazor.Core.ESBuild/dymaptic.GeoBlazor.Core.ESBuild.csproj new file mode 100644 index 000000000..897eece33 --- /dev/null +++ b/src/dymaptic.GeoBlazor.Core.ESBuild/dymaptic.GeoBlazor.Core.ESBuild.csproj @@ -0,0 +1,16 @@ + + + net8.0 + + + + + + + + + + + \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/ESBuildGenerator.cs b/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/ESBuildGenerator.cs new file mode 100644 index 000000000..8836ece24 --- /dev/null +++ b/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/ESBuildGenerator.cs @@ -0,0 +1,121 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + + +namespace dymaptic.GeoBlazor.Core.SourceGenerator.Shared; + +[SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1035:Do not use APIs banned for analyzers")] +public static class ESBuildGenerator +{ + public static Dictionary ConfigSelector(AnalyzerConfigOptionsProvider configProvider, + CancellationToken _) + { + Dictionary options = []; + + if (configProvider.GlobalOptions.TryGetValue($"{BUILD_PROPERTY}.{GB_BUILD_TOOLS_PATH}", + out var gbBuildToolsPath)) + { + options[GB_BUILD_TOOLS_PATH] = gbBuildToolsPath; + } + + if (configProvider.GlobalOptions.TryGetValue($"{BUILD_PROPERTY}.{DESIGN_TIME_BUILD}", + out var designTimeBuild)) + { + options[DESIGN_TIME_BUILD] = designTimeBuild; + } + + return options; + } + + public static void FilesChanged(SourceProductionContext context, + (ImmutableArray Files, Dictionary Options) pipeline) + { + if (pipeline.Files.Length == 0) + { + ProcessHelper.Log(nameof(ESBuildGenerator), + "ESBuild Source Generator no files changed.", + DiagnosticSeverity.Info, + context); + + return; + } + + var parsedOptions = ParseOptions(pipeline.Options, context); + + if (parsedOptions is null) + { + return; + } + + var options = parsedOptions.Value; + + if (options.GBBuildToolsPath is null) + { + ProcessHelper.Log(nameof(ESBuildGenerator), + "ESBuild Source Generation skipped due to missing configuration settings.", + DiagnosticSeverity.Info, + context); + + return; + } + + ClearESBuildLocks(options, context); + } + + private static ESBuildGeneratorOptions? ParseOptions(Dictionary options, + SourceProductionContext context) + { + try + { + // Use TryGetValue to safely access dictionary keys + options.TryGetValue(GB_BUILD_TOOLS_PATH, out var gbBuildToolsPath); + + // For boolean values, provide defaults if not found + var designTimeBuild = false; + + if (options.TryGetValue(DESIGN_TIME_BUILD, out var designTimeBuildStr)) + { + bool.TryParse(designTimeBuildStr, out designTimeBuild); + } + + return new ESBuildGeneratorOptions(gbBuildToolsPath, designTimeBuild); + } + catch (Exception ex) + { + ProcessHelper.Log(nameof(ESBuildGenerator), + $"Error parsing configuration settings: {ex.Message}", + DiagnosticSeverity.Error, context); + + return null; + } + } + + private static void ClearESBuildLocks(ESBuildGeneratorOptions options, + SourceProductionContext context) + { + string[] args = options.DesignTimeBuild + ? ["ESBuildClearLocks.dll"] + + // only remove stale files during a full build + : ["ESBuildClearLocks.dll", "--stale-files"]; + + _ = Task.Run(async () => await ProcessHelper.Execute("Clear Locks", + options.GBBuildToolsPath!, "dotnet", + args, context, false, null)); + + ProcessHelper.Log(nameof(ESBuildGenerator), + "Cleared ESBuild Process Locks", + DiagnosticSeverity.Info, + context); + } + + private const string GB_BUILD_TOOLS_PATH = "GBBuildToolsPath"; + private const string DESIGN_TIME_BUILD = "DesignTimeBuild"; + private const string BUILD_PROPERTY = "build_property"; +} + +public record struct ESBuildGeneratorOptions( + string? GBBuildToolsPath, + bool DesignTimeBuild); \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/ProcessHelper.cs b/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/ProcessHelper.cs index 5b03afc2f..605bb5c80 100644 --- a/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/ProcessHelper.cs +++ b/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/ProcessHelper.cs @@ -15,6 +15,11 @@ namespace dymaptic.GeoBlazor.Core.SourceGenerator.Shared; /// public static class ProcessHelper { + /// + /// Set this to true to bypass actual process execution and script execution for testing purposes. + /// + public static bool TestBypass { get; set; } + /// /// Executes a PowerShell script file with the specified arguments. /// @@ -22,12 +27,17 @@ public static class ProcessHelper /// The working directory for the script execution. /// The name of the PowerShell script file to execute. /// Command-line arguments to pass to the script. - /// A StringBuilder to accumulate log output. /// The SourceProductionContext for diagnostic reporting. + /// + /// Whether to show console output for the process. If true, verbose output is shown. + /// + /// + /// The session ID to use for logging. + /// /// Optional environment variables to set for the process. public static async Task RunPowerShellScript(string processName, string workingDirectory, - string powershellScriptName, string[] arguments, StringBuilder logBuilder, SourceProductionContext context, - Dictionary? environmentVariables = null) + string powershellScriptName, string[] arguments, SourceProductionContext context, bool showConsole, + string? sessionId, Dictionary? environmentVariables = null) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { @@ -50,7 +60,7 @@ public static async Task RunPowerShellScript(string processName, string workingD ]; } - await Execute(processName, workingDirectory, "pwsh", arguments, logBuilder, context, + await Execute(processName, workingDirectory, "pwsh", arguments, context, showConsole, sessionId, environmentVariables); } @@ -60,11 +70,16 @@ await Execute(processName, workingDirectory, "pwsh", arguments, logBuilder, cont /// A descriptive name for the process, used in logging. /// The working directory for the command execution. /// The PowerShell command to execute. - /// A StringBuilder to accumulate log output. /// The SourceProductionContext for diagnostic reporting. + /// + /// Whether to show console output for the process. If true, verbose output is shown. + /// + /// + /// The session ID to use for logging. + /// /// Optional environment variables to set for the process. public static async Task RunPowerShellCommand(string processName, string workingDirectory, - string[] arguments, StringBuilder logBuilder, SourceProductionContext context, + string[] arguments, SourceProductionContext context, bool showConsole, string? sessionId, Dictionary? environmentVariables = null) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -81,7 +96,7 @@ public static async Task RunPowerShellCommand(string processName, string working ]; } - await Execute(processName, workingDirectory, "pwsh", arguments, logBuilder, context, + await Execute(processName, workingDirectory, "pwsh", arguments, context, showConsole, sessionId, environmentVariables); } @@ -92,25 +107,40 @@ await Execute(processName, workingDirectory, "pwsh", arguments, logBuilder, cont /// The working directory for the process execution. /// The executable file name. If null, uses the platform-specific shell command. /// Command-line arguments to pass to the process. - /// A StringBuilder to accumulate log output. /// The SourceProductionContext for diagnostic reporting. + /// + /// Whether to show console output for the process. If true, verbose output is shown. + /// + /// + /// The session ID to use for logging. + /// /// Optional environment variables to set for the process. /// Thrown when the process exits with a non-zero exit code. public static async Task Execute(string processName, string workingDirectory, string? fileName, - string[] shellArguments, StringBuilder logBuilder, SourceProductionContext context, + string[] shellArguments, SourceProductionContext context, bool showConsole, string? sessionId, Dictionary? environmentVariables = null) { fileName ??= shellCommand; - StringBuilder outputBuilder = new(); + Log(processName, $"Starting process execution: {processName} {string.Join(" ", shellArguments)}", + DiagnosticSeverity.Info, context, showConsole, sessionId); + + if (TestBypass) + { + Log(processName, $"Command '{fileName} {string.Join(" ", shellArguments)}' completed successfully.", + DiagnosticSeverity.Info, context, showConsole, sessionId); + + return; + } + int? processId = null; int? exitCode = null; context.CancellationToken.Register(() => { - logBuilder.AppendLine($"{processName}: Command execution cancelled."); - logBuilder.AppendLine(outputBuilder.ToString()); - outputBuilder.Clear(); + Log(processName, "Command execution cancelled.", + DiagnosticSeverity.Info, context, + showConsole, sessionId); }); Command cmd = Cli.Wrap(fileName) @@ -119,49 +149,70 @@ public static async Task Execute(string processName, string workingDirectory, st .WithValidation(CommandResultValidation.None) .WithEnvironmentVariables(environmentVariables ?? new Dictionary()); + bool retry = false; + + // build up the input in case we need it for writing to an error + StringBuilder errorBuilder = new(); + bool errorFound = false; + await foreach (var cmdEvent in cmd.ListenAsync(context.CancellationToken)) { switch (cmdEvent) { case StartedCommandEvent started: processId = started.ProcessId; - outputBuilder.AppendLine($" - {processName} Process started: {started.ProcessId}"); - outputBuilder.AppendLine($" - {processName} - PID {processId}: Executing command: {fileName} { - string.Join(" ", shellArguments)}"); + Log(processName, $"Process {processId} started.", + DiagnosticSeverity.Info, context, + showConsole, sessionId); break; case StandardOutputCommandEvent stdOut: string line = stdOut.Text.Trim(); - outputBuilder.AppendLine($" - {processName} - PID {processId}: [stdout] {line}"); + + Log(processName, $"Process {processId} - [stdout] {line}", + DiagnosticSeverity.Info, context, + showConsole, sessionId); + + if (line.Contains("error", StringComparison.OrdinalIgnoreCase) || errorFound) + { + errorBuilder.AppendLine(line); + errorFound = true; + } + + if (fileName == "dotnet" && stdOut.Text.Contains("The process cannot access the file")) + { + retry = true; + } break; case StandardErrorCommandEvent stdErr: - outputBuilder.AppendLine($" - {processName} - PID {processId}: [stderr] {stdErr.Text}"); + Log(processName, $"Process {processId} - [stderr] {stdErr.Text}", + DiagnosticSeverity.Warning, context, + showConsole, sessionId); + + errorBuilder.AppendLine(stdErr.Text); + + if (fileName == "dotnet" && stdErr.Text.Contains("The process cannot access the file")) + { + retry = true; + } break; case ExitedCommandEvent exited: exitCode = exited.ExitCode; - outputBuilder.AppendLine($" - {processName} - PID {processId}: Process exited with code: { - exited.ExitCode}"); + Log(processName, $"Process {processId} exited with code: {exited.ExitCode}", + DiagnosticSeverity.Info, context, + showConsole, sessionId); break; } } - // Append any accumulated output to the log - if (outputBuilder.Length > 0) - { - logBuilder.AppendLine(outputBuilder.ToString()); - } - if (exitCode != 0) { - var response = logBuilder.ToString(); - Log(processName, response, DiagnosticSeverity.Info, context); - - if (response.Contains("The process cannot access the file") && (fileName == "dotnet")) + if (retry) { var programName = shellArguments[1]; // dotnet[fileName] run[arg[0]] ESBuild.cs[arg[1]] @@ -183,24 +234,23 @@ public static async Task Execute(string processName, string workingDirectory, st await Task.Delay(500); - await Execute(processName, workingDirectory, fileName, shellArguments, logBuilder, context, + await Execute(processName, workingDirectory, fileName, shellArguments, context, showConsole, sessionId, environmentVariables); return; } Log(processName, - $"Command '{fileName} {string.Join(" ", shellArguments)}' failed with exit code {exitCode}.", + $"Command '{fileName} {string.Join(" ", shellArguments)}' failed with exit code {exitCode}. { + errorBuilder}", DiagnosticSeverity.Error, context); return; } - // Return the standard output if the process completed normally - logBuilder.AppendLine($" - {processName}: Command '{string.Join(" ", shellArguments) - }' completed successfully on process {processId - }."); + Log(processName, $"Command '{fileName} {string.Join(" ", shellArguments)}' completed successfully.", + DiagnosticSeverity.Info, context, showConsole, sessionId); } /// @@ -314,12 +364,20 @@ private static void ShowOrUpdateConsole(string title, string message, string? se } } + var os = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? "win" + : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) + ? "osx" + : "linux"; + var arch = RuntimeInformation.OSArchitecture.ToString().ToLowerInvariant(); + var buildScriptPath = Path.GetFullPath(Path.Combine( Path.GetDirectoryName( callerFilePath)!, // GeoBlazor/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared "..", // GeoBlazor/src "..", // GeoBlazor Core repo root - "build-tools")); + "build-tools", + $"{os}-{arch}")); string[] args = [ diff --git a/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/ProtobufDefinitionsGenerator.cs b/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/ProtobufDefinitionsGenerator.cs index 76b3318dd..96b444222 100644 --- a/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/ProtobufDefinitionsGenerator.cs +++ b/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/ProtobufDefinitionsGenerator.cs @@ -36,7 +36,6 @@ public static Dictionary UpdateProtobufDefinitio "\n" => "\\n", _ => match.Value }); - StringBuilder logBuilder = new(); var scriptPath = Path.Combine(corePath, "copyProtobuf.ps1"); @@ -45,15 +44,10 @@ public static Dictionary UpdateProtobufDefinitio ProcessHelper.RunPowerShellScript("Copy Protobuf Definitions", corePath, scriptPath, ["-Content", encoded], - logBuilder, context) + context, showDialog, sessionId) .GetAwaiter() .GetResult(); - ProcessHelper.Log(nameof(ProtobufDefinitionsGenerator), - logBuilder.ToString(), - DiagnosticSeverity.Info, - context, sessionId: sessionId); - ProcessHelper.Log(nameof(ProtobufDefinitionsGenerator), "Protobuf definitions updated successfully.", DiagnosticSeverity.Info, diff --git a/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/SerializationGenerator.cs b/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/SerializationGenerator.cs index 27d92c4d0..aea7c8b6b 100644 --- a/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/SerializationGenerator.cs +++ b/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/SerializationGenerator.cs @@ -1,4 +1,5 @@ using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Collections.Immutable; using System.Text; @@ -121,36 +122,38 @@ private static string GenerateExtensionMethods(Dictionary /// Convenience method to deserialize an to a specific type via protobuf. /// - public static async Task ReadJsStreamReferenceAsProtobuf(this IJSStreamReference jsStreamReference, - Type returnType, long maxAllowedSize = 1_000_000_000) + public static partial async Task ReadJsStreamReferenceAsProtobuf(this IJSStreamReference jsStreamReference, + Type returnType, long? maxAllowedSize, CancellationToken cancellationToken) { - await using Stream stream = await jsStreamReference.OpenReadStreamAsync(maxAllowedSize); + maxAllowedSize ??= 1_000_000_000L; + await using Stream stream = await jsStreamReference.OpenReadStreamAsync(maxAllowedSize.Value); using MemoryStream memoryStream = new(); await stream.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); - + string typeName = returnType.Name.Replace("SerializationRecord", ""); - switch (typeName) + switch (typeName) { """); StringBuilder readJsProtoCollectionStreamRefMethod = new(""" /// - /// Convenience method to deserialize an to a specific coolection type via protobuf. + /// Convenience method to deserialize an to a specific collection type via protobuf. /// - public static async Task ReadJsStreamReferenceAsProtobufCollection(this IJSStreamReference jsStreamReference, - Type returnType, long maxAllowedSize = 1_000_000_000) + public static partial async Task ReadJsStreamReferenceAsProtobufCollection(this IJSStreamReference jsStreamReference, + Type returnType, long? maxAllowedSize, CancellationToken cancellationToken) { - await using Stream stream = await jsStreamReference.OpenReadStreamAsync(maxAllowedSize); + maxAllowedSize ??= 1_000_000_000L; + await using Stream stream = await jsStreamReference.OpenReadStreamAsync(maxAllowedSize.Value); using MemoryStream memoryStream = new(); await stream.CopyToAsync(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); - + string typeName = returnType.Name.Replace("SerializationRecord", ""); - switch (typeName) + switch (typeName) { """); @@ -181,7 +184,7 @@ private static string GenerateExtensionMethods(Dictionary /// Convenience method to generate a Protobuf serialized parameter. /// - public static object ToProtobufParameter(this object value, Type serializableType, bool isServer) + public static partial object ToProtobufParameter(this object value, Type serializableType, bool isServer) { MemoryStream memoryStream = new(); switch (serializableType.Name) @@ -193,7 +196,7 @@ public static object ToProtobufParameter(this object value, Type serializableTyp /// /// Convenience method to generate a Protobuf serialized collection parameter. /// - public static object ToProtobufCollectionParameter(this IList items, Type serializableType, bool isServer) + public static partial object ToProtobufCollectionParameter(this IList items, Type serializableType, bool isServer) { MemoryStream memoryStream = new(); string typeName = $"{serializableType.Name}Collection"; @@ -229,32 +232,31 @@ public static object ToProtobufCollectionParameter(this IList items, Type serial readJsProtoStreamRefMethod.AppendLine($$""" case "{{protoSerializableType}}": - {{serializationRecordType}} {{variableName}} = + {{serializationRecordType}} {{variableName}} = Serializer.Deserialize<{{serializationRecordType }}>(memoryStream); if ({{variableName}}.IsNull) { - return default!; + return null; } - return (T?)(object?){{variableName - }}?.FromSerializationRecord(); + return {{variableName}}.FromSerializationRecord(); """); readJsProtoCollectionStreamRefMethod.AppendLine($$""" case "{{protoSerializableType}}": {{serializationCollectionRecordType}} {{ - variableName}} = + variableName}} = Serializer.Deserialize<{{ serializationCollectionRecordType }}>(memoryStream); if ({{variableName}}.IsNull) { - return default!; + return null; } - return (T?)(object?){{variableName + return {{variableName }} - ?.Items?.Select(i => i.FromSerializationRecord()).Cast<{{ - protoSerializableType}}>().ToArray(); + .Items?.Select(i => i.FromSerializationRecord()).Cast().ToArray(); + """); if (definition.Name == "Attribute") @@ -295,15 +297,15 @@ public static object ToProtobufCollectionParameter(this IList items, Type serial readJsProtoStreamRefMethod.AppendLine(""" } - - return default!; + + return null; } """); readJsProtoCollectionStreamRefMethod.AppendLine(""" } - - return default!; + + return null; } """); @@ -390,15 +392,25 @@ private static string GenerateSerializableMethodRecords( """); } - foreach (var classGroup in serializedMethodsCollection.GroupBy(m => m.ClassName)) + List> classGroups = serializedMethodsCollection + .GroupBy(m => m.ClassName) + .ToList(); + + for (int i = 0; i < classGroups.Count; i++) { + IGrouping classGroup = classGroups[i]; + outputBuilder.AppendLine($$""" ["{{classGroup.Key}}"] = [ """); - foreach (var methodRecord in classGroup) + List methodRecords = classGroup.ToList(); + + for (int j = 0; j < methodRecords.Count; j++) { + SerializableMethodRecord methodRecord = methodRecords[j]; + if (methodRecord.Parameters.Values.Contains("T") || methodRecord.Parameters.Values.Contains("T?") || (methodRecord.ReturnType == "T") @@ -416,58 +428,58 @@ private static string GenerateSerializableMethodRecords( [ """); - foreach (var param in methodRecord.Parameters) + List parameters = methodRecord.Parameters.Values.ToList(); + + for (int k = 0; k < parameters.Count; k++) { - bool isNullable = param.Value.EndsWith("?"); - string value = isNullable ? param.Value.TrimEnd('?') : param.Value; - string isNullableText = isNullable ? "true" : "false"; - string? collectionType = null; + string param = parameters[k]; + outputBuilder.Append(GenerateSerializableParameterRecord(param, 24)); - if (value.EndsWith("[]")) + if (k < parameters.Count - 1) { - collectionType = value.Replace("[]", ""); + outputBuilder.AppendLine(","); } - else if (value.Contains("<") && value.Contains(">")) + else { - int genericStart = value.IndexOf("<", StringComparison.OrdinalIgnoreCase); - collectionType = value.Substring(genericStart + 1, value.Length - genericStart - 2); + outputBuilder.AppendLine(); } - - string collectionText = collectionType is null - ? "null" - : $"typeof({collectionType})"; - - outputBuilder.AppendLine($" new SerializableParameterRecord(typeof({value - }), {isNullableText}, {collectionText}),"); } if (methodRecord.ReturnType != null) { - string returnValue = methodRecord.ReturnType.TrimEnd('?'); - - bool isCollectionReturn = returnValue.EndsWith("[]") || - (returnValue.Contains("<") && returnValue.Contains(">")); - - string singleType = isCollectionReturn - ? returnValue.Contains("<") && returnValue.Contains(">") - ? $"typeof({returnValue.Substring( - returnValue.IndexOf("<", StringComparison.OrdinalIgnoreCase) + 1, - returnValue.Length - returnValue.IndexOf("<", StringComparison.OrdinalIgnoreCase) - 2) - })" - : $"typeof({returnValue.Replace("[]", "")})" - : "null"; - string isNullable = methodRecord.ReturnType.EndsWith("?") ? "true" : "false"; - - outputBuilder.AppendLine($" ], new SerializableParameterRecord(typeof({ - returnValue}), {isNullable}, {singleType})),"); + outputBuilder.AppendLine(" ],"); + outputBuilder.Append(GenerateSerializableParameterRecord(methodRecord.ReturnType, 20)); + + if (j < methodRecords.Count - 1) + { + outputBuilder.AppendLine("),"); + } + else + { + outputBuilder.AppendLine(")"); + } } else { - outputBuilder.AppendLine(" ])),"); + if (j < methodRecords.Count - 1) + { + outputBuilder.AppendLine(" ]),"); + } + else + { + outputBuilder.AppendLine(" ]"); + } } } - outputBuilder.AppendLine(" ],"); + if (i < classGroups.Count - 1) + { + outputBuilder.AppendLine(" ],"); + } + else + { + outputBuilder.AppendLine(" ]"); + } } outputBuilder.AppendLine(" };"); @@ -475,6 +487,42 @@ private static string GenerateSerializableMethodRecords( return outputBuilder.ToString(); } + private static string GenerateSerializableParameterRecord(string value, int indent) + { + bool isNullable = value.EndsWith("?"); + string trimmedValue = value.Replace("?", ""); // replace all ?, even in the inner single type + string isNullableText = isNullable ? "true" : "false"; + string? singleType = null; + + if (trimmedValue.EndsWith("[]")) // check against the trimmed version without ? + { + // use untrimmed value again to restore ? in the single types + int arrayStart = value.IndexOf("[", StringComparison.OrdinalIgnoreCase); + singleType = value.Substring(0, arrayStart); + } + else if (value.Contains("<") && trimmedValue.Contains(">")) + { + // use param.Value again to restore ? in the single types + int genericStart = value.IndexOf("<", StringComparison.OrdinalIgnoreCase); + singleType = value.Substring(genericStart + 1, trimmedValue.Length - genericStart - 2); + } + + bool singleTypeIsNullable = singleType?.EndsWith("?") == true; + string singleTypeIsNullableText = singleTypeIsNullable ? "true" : "false"; + singleType = singleType?.Replace("?", ""); + + string collectionText = singleType is null + ? "null" + : $"typeof({singleType})"; + + string padding = new string(' ', indent); + + return $""" + {padding}new SerializableParameterRecord(typeof({trimmedValue}), {isNullableText}, + {padding} {collectionText}, {singleTypeIsNullableText}) + """; + } + private static List ToSerializableMethodRecords(this BaseTypeDeclarationSyntax typeSyntax) { List methods = typeSyntax @@ -497,10 +545,21 @@ private static List ToSerializableMethodRecords(this B returnType = returnType.Substring(bracketIndex + 1, returnType.Length - bracketIndex - 2); } + // For static extension methods, skip the 'this' parameter + var parameters = method.Modifiers + .Any(m => m.IsKind(SyntaxKind.StaticKeyword)) + ? method.ParameterList.Parameters + .Where(p => !p.Modifiers + .Any(m => m.IsKind(SyntaxKind.ThisKeyword))) + .ToDictionary(p => p.Identifier.Text, + p => p.Type!.ToString()) + : method.ParameterList.Parameters + .ToDictionary(p => p.Identifier.Text, + p => p.Type!.ToString()); + SerializableMethodRecord record = new(typeSyntax.Identifier.Text, method.Identifier.Text, - method.ParameterList.Parameters.ToDictionary(p => p.Identifier.Text, - p => p.Type!.ToString()), + parameters, returnType); methodRecords.Add(record); diff --git a/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/dymaptic.GeoBlazor.Core.SourceGenerator.Shared.csproj b/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/dymaptic.GeoBlazor.Core.SourceGenerator.Shared.csproj index 3737db384..4c762f2d3 100644 --- a/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/dymaptic.GeoBlazor.Core.SourceGenerator.Shared.csproj +++ b/src/dymaptic.GeoBlazor.Core.SourceGenerator.Shared/dymaptic.GeoBlazor.Core.SourceGenerator.Shared.csproj @@ -12,6 +12,6 @@ - + diff --git a/src/dymaptic.GeoBlazor.Core.SourceGenerator/CoreESBuildGenerator.cs b/src/dymaptic.GeoBlazor.Core.SourceGenerator/CoreESBuildGenerator.cs new file mode 100644 index 000000000..93807bdc0 --- /dev/null +++ b/src/dymaptic.GeoBlazor.Core.SourceGenerator/CoreESBuildGenerator.cs @@ -0,0 +1,34 @@ +using dymaptic.GeoBlazor.Core.SourceGenerator.Shared; +using Microsoft.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; + + +namespace dymaptic.GeoBlazor.Core.SourceGenerator; + +/// +/// Triggers the ESBuild build process for the GeoBlazor project, so that your JavaScript code is up to date. +/// +[Generator] +[SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1035:Do not use APIs banned for analyzers")] +public class CoreESBuildGenerator : IIncrementalGenerator +{ + /// + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Tracks all TypeScript source files in the Scripts directories of Core and Pro. + // This will trigger the build any time a TypeScript file is added, removed, or changed. + var tsFilesProvider = context + .AdditionalTextsProvider + .Where(static text => text.Path.Contains("Scripts") && text.Path.EndsWith(".ts")) + .Collect(); + + // Reads the MSBuild properties to get the project directory and configuration. + var optionsProvider = + context.AnalyzerConfigOptionsProvider.Select(ESBuildGenerator.ConfigSelector); + + var combined = + tsFilesProvider.Combine(optionsProvider); + + context.RegisterSourceOutput(combined, (ctx, pipeline) => ESBuildGenerator.FilesChanged(ctx, pipeline)); + } +} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core.SourceGenerator/ESBuildGenerator.cs b/src/dymaptic.GeoBlazor.Core.SourceGenerator/ESBuildGenerator.cs deleted file mode 100644 index ea8489806..000000000 --- a/src/dymaptic.GeoBlazor.Core.SourceGenerator/ESBuildGenerator.cs +++ /dev/null @@ -1,279 +0,0 @@ -using dymaptic.GeoBlazor.Core.SourceGenerator.Shared; -using Microsoft.CodeAnalysis; -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using System.Text; - - -namespace dymaptic.GeoBlazor.Core.SourceGenerator; - -/// -/// Triggers the ESBuild build process for the GeoBlazor project, so that your JavaScript code is up to date. -/// -[Generator] -[SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1035:Do not use APIs banned for analyzers")] -public class ESBuildGenerator : IIncrementalGenerator -{ - private static string? BuildToolsPath => _corePath is null - ? null - : Path.GetFullPath(Path.Combine(_corePath, "..", "..", "build-tools")); - - /// - public void Initialize(IncrementalGeneratorInitializationContext context) - { - // Tracks all TypeScript source files in the Scripts directories of Core and Pro. - // This will trigger the build any time a TypeScript file is added, removed, or changed. - IncrementalValueProvider> tsFilesProvider = context.AdditionalTextsProvider - .Where(static text => text.Path.Contains("Scripts") - && text.Path.EndsWith(".ts")) - .Collect(); - - // Reads the MSBuild properties to get the project directory and configuration. - IncrementalValueProvider<(string?, string?, string?, string?)> optionsProvider = - context.AnalyzerConfigOptionsProvider.Select((configProvider, _) => - { - configProvider.GlobalOptions.TryGetValue("build_property.CoreProjectPath", - out string? projectDirectory); - - configProvider.GlobalOptions.TryGetValue("build_property.Configuration", - out string? configuration); - - configProvider.GlobalOptions.TryGetValue("build_property.PipelineBuild", - out string? pipelineBuild); - - configProvider.GlobalOptions.TryGetValue("build_property.DesignTimeBuild", - out var designTimeBuild); - - return (projectDirectory, configuration, pipelineBuild, designTimeBuild); - }); - - var - combined = - tsFilesProvider.Combine(optionsProvider); - - context.RegisterSourceOutput(combined, FilesChanged); - } - - private void FilesChanged(SourceProductionContext context, - (ImmutableArray Files, - (string? ProjectDirectory, string? Configuration, string? PipelineBuild, string? DesignTimeBuild) Options) - pipeline) - { - if (!SetProjectDirectoryAndConfiguration(pipeline.Options, context)) - { - return; - } - - ProcessHelper.Log(nameof(ESBuildGenerator), - "ESBuild Source Generation triggered.", - DiagnosticSeverity.Info, - context); - - if (pipeline.Options.PipelineBuild == "true") - { - // If the pipeline build is enabled, we skip the ESBuild process. - // This is to avoid race conditions where the files are not ready on time, and we do the build separately. - ProcessHelper.Log(nameof(ESBuildGenerator), - "Skipping ESBuild process as PipelineBuild is set to true.", - DiagnosticSeverity.Info, - context); - - return; - } - - if (pipeline.Files.Length > 0) - { - LaunchESBuild(context); - } - } - - private bool SetProjectDirectoryAndConfiguration( - (string? ProjectDirectory, string? Configuration, string? _, string? DesignTimeBuild) options, - SourceProductionContext context) - { - string? projectDirectory = options.ProjectDirectory; - - if (projectDirectory is not null) - { - _corePath = Path.GetFullPath(projectDirectory); - - ProcessHelper.Log(nameof(ESBuildGenerator), - $"Project directory set to {_corePath}", - DiagnosticSeverity.Info, - context); - - if (_corePath.Contains("GeoBlazor.Pro")) - { - // we are inside the Pro submodule, we should also set the Pro path to build the Pro JavaScript files - string path = _corePath; - - while (!path.EndsWith("GeoBlazor.Pro")) - { - // move up the directory tree until we find the GeoBlazor.Pro directory - path = Path.GetDirectoryName(path)!; - } - - // set the pro path to the src/dymaptic.GeoBlazor.Pro directory - _proPath = Path.GetFullPath(Path.Combine(path, "src", "dymaptic.GeoBlazor.Pro")); - } - } - else - { - ProcessHelper.Log(nameof(ESBuildGenerator), - "Invalid project directory.", - DiagnosticSeverity.Error, - context); - - return false; - } - - if (options.Configuration is { } configuration) - { - _configuration = configuration; - _isDesignTimeBuild = options.DesignTimeBuild == "true"; - - return true; - } - - ProcessHelper.Log(nameof(ESBuildGenerator), - "Could not parse configuration setting, invalid configuration.", - DiagnosticSeverity.Error, - context); - - return false; - } - - private void LaunchESBuild(SourceProductionContext context) - { - context.CancellationToken.ThrowIfCancellationRequested(); - - ProcessHelper.Log(nameof(ESBuildGenerator), - "Starting Core ESBuild process...", - DiagnosticSeverity.Info, - context); - - StringBuilder logBuilder = new StringBuilder(DateTime.Now.ToLongTimeString()); - logBuilder.AppendLine(); - logBuilder.AppendLine("Starting Core ESBuild process..."); - - try - { - List tasks = []; - bool buildSuccess = false; - bool proBuildSuccess = false; - - // gets the ESBuild.cs script - // Only show dialog on full compilation builds, not design-time builds - string[] coreArgs = - [ - "ESBuild.dll", - "-c", _configuration!, // set config for ESBuild - "-d" // show dialog - ]; - - if (!_isDesignTimeBuild) - { - coreArgs = [..coreArgs, "-v"]; // show verbose output - } - - tasks.Add(Task.Run(async () => - { - await ProcessHelper.Execute("Core", - BuildToolsPath!, "dotnet", - coreArgs, logBuilder, context); - buildSuccess = true; - })); - - if (_proPath is not null) - { - logBuilder.AppendLine("Starting Pro ESBuild process..."); - - string[] proArgs = - [ - "ESBuild.dll", - "-c", _configuration!, // set config for ESBuild - "-d", // show dialog - "--pro" // build for Pro project - ]; - - if (!_isDesignTimeBuild) - { - proArgs = [..proArgs, "-v"]; // show verbose output - } - - tasks.Add(Task.Run(async () => - { - await ProcessHelper.Execute("Pro", - BuildToolsPath!, "dotnet", - proArgs, logBuilder, context); - proBuildSuccess = true; - })); - } - - Task.WhenAll(tasks).GetAwaiter().GetResult(); - - if (!buildSuccess) - { - ProcessHelper.Log(nameof(ESBuildGenerator), - $"Core ESBuild process failed\r\n{logBuilder}", - DiagnosticSeverity.Error, - context); - - return; - } - - logBuilder.AppendLine("Core ESBuild process completed successfully."); - logBuilder.AppendLine(); - - if (_proPath is not null) - { - if (!proBuildSuccess) - { - ProcessHelper.Log(nameof(ESBuildGenerator), - $"Pro ESBuild process failed\r\n{logBuilder}", - DiagnosticSeverity.Error, - context); - - return; - } - - logBuilder.AppendLine("Pro ESBuild process completed successfully."); - logBuilder.AppendLine(); - } - - ProcessHelper.Log(nameof(ESBuildGenerator), - logBuilder.ToString(), - DiagnosticSeverity.Info, - context); - } - catch (Exception ex) - { - ProcessHelper.Log(nameof(ESBuildGenerator), - $"An error occurred while running ESBuild: {ex.Message}\r\n{ex.StackTrace}", - DiagnosticSeverity.Error, - context); - - ClearESBuildLocks(context); - } - } - - private void ClearESBuildLocks(SourceProductionContext context) - { - StringBuilder logBuilder = new(); - - _ = Task.Run(async () => await ProcessHelper.Execute("Clear Locks", - BuildToolsPath!, "dotnet", - ["ESBuildClearLocks.dll"], - logBuilder, context)); - - ProcessHelper.Log(nameof(ESBuildGenerator), - "Cleared ESBuild Process Locks", - DiagnosticSeverity.Info, - context); - } - - private static string? _corePath; - private static string? _proPath; - private static string? _configuration; - private static bool _isDesignTimeBuild; -} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core.SourceGenerator/ProtobufSourceGenerator.cs b/src/dymaptic.GeoBlazor.Core.SourceGenerator/ProtobufSourceGenerator.cs index c1c6e068c..7cb8df618 100644 --- a/src/dymaptic.GeoBlazor.Core.SourceGenerator/ProtobufSourceGenerator.cs +++ b/src/dymaptic.GeoBlazor.Core.SourceGenerator/ProtobufSourceGenerator.cs @@ -22,43 +22,69 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.SyntaxProvider.CreateSyntaxProvider(static (syntaxNode, _) => syntaxNode is ClassDeclarationSyntax or StructDeclarationSyntax or RecordDeclarationSyntax && (((BaseTypeDeclarationSyntax)syntaxNode).AttributeLists.SelectMany(a => a.Attributes) - .Any(a => a.Name.ToString() is ProtoContractAttribute or ProtoSerializableAttribute) + .Any(a => a.Name.ToString() is PROTO_CONTRACT_ATTRIBUTE or PROTO_SERIALIZABLE_ATTRIBUTE) || syntaxNode.ChildNodes() .OfType() .Any(m => m.AttributeLists .SelectMany(a => a.Attributes) - .Any(attr => attr.Name.ToString() == SerializedMethodAttributeName))), + .Any(attr => attr.Name.ToString() == SERIALIZED_METHOD_ATTRIBUTE_NAME))), static (context, _) => (BaseTypeDeclarationSyntax)context.Node) .Collect(); // Reads the MSBuild properties to get the project directory. - IncrementalValueProvider<(string?, string?)> optionsProvider = + IncrementalValueProvider> optionsProvider = context.AnalyzerConfigOptionsProvider.Select((configProvider, _) => { - configProvider.GlobalOptions.TryGetValue("build_property.CoreProjectPath", - out var projectDirectory); + Dictionary options = []; - configProvider.GlobalOptions.TryGetValue("build_property.PipelineBuild", - out var pipelineBuild); + if (configProvider.GlobalOptions.TryGetValue("build_property.CoreProjectPath", + out var projectDirectory)) + { + options["CoreProjectPath"] = projectDirectory; + } + + if (configProvider.GlobalOptions.TryGetValue("build_property.DesignTimeBuild", + out var designTimeBuild)) + { + options["DesignTimeBuild"] = designTimeBuild; + } - return (projectDirectory, pipelineBuild); + if (configProvider.GlobalOptions.TryGetValue("build_property.ShowScriptDialogs", + out var showDialog)) + { + options["ShowScriptDialogs"] = showDialog; + } + + return options; }); - IncrementalValueProvider<(ImmutableArray Left, (string?, string?) Right)> combined = + IncrementalValueProvider<(ImmutableArray Left, + Dictionary Right)> combined = typeProvider.Combine(optionsProvider); context.RegisterSourceOutput(combined, FilesChanged); } private void FilesChanged(SourceProductionContext context, - (ImmutableArray Types, (string? ProjectDirectory, string? PipelineBuild) Options) + (ImmutableArray Types, Dictionary Options) pipeline) { - _corePath = pipeline.Options.ProjectDirectory; - var showDialog = pipeline.Options.PipelineBuild != "true"; + pipeline.Options.TryGetValue("CoreProjectPath", out _corePath); + pipeline.Options.TryGetValue("DesignTimeBuild", out string? designTimeBuildString); + + bool designTimeBuild = designTimeBuildString is not null + && bool.TryParse(designTimeBuildString, out bool designTimeBuildBool) + && designTimeBuildBool; + + pipeline.Options.TryGetValue("ShowScriptDialogs", out var showDialogString); + + bool showDialog = designTimeBuild + && showDialogString is not null + && bool.TryParse(showDialogString, out bool showDialogBool) + && showDialogBool; // Generate a unique session ID for this build session - var sessionId = $"{nameof(ProtobufSourceGenerator)}_{Guid.NewGuid():N}"; + string sessionId = $"{nameof(ProtobufSourceGenerator)}_{Guid.NewGuid():N}"; if (pipeline.Types.Length > 0) { @@ -100,7 +126,7 @@ private void FilesChanged(SourceProductionContext context, } private static string? _corePath; - private const string ProtoContractAttribute = "ProtoContract"; - private const string ProtoSerializableAttribute = "ProtobufSerializable"; - private const string SerializedMethodAttributeName = "SerializedMethod"; + private const string PROTO_CONTRACT_ATTRIBUTE = "ProtoContract"; + private const string PROTO_SERIALIZABLE_ATTRIBUTE = "ProtobufSerializable"; + private const string SERIALIZED_METHOD_ATTRIBUTE_NAME = "SerializedMethod"; } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core.SourceGenerator/dymaptic.GeoBlazor.Core.SourceGenerator.csproj b/src/dymaptic.GeoBlazor.Core.SourceGenerator/dymaptic.GeoBlazor.Core.SourceGenerator.csproj index f84022e51..c0e63c031 100644 --- a/src/dymaptic.GeoBlazor.Core.SourceGenerator/dymaptic.GeoBlazor.Core.SourceGenerator.csproj +++ b/src/dymaptic.GeoBlazor.Core.SourceGenerator/dymaptic.GeoBlazor.Core.SourceGenerator.csproj @@ -12,7 +12,7 @@ - diff --git a/src/dymaptic.GeoBlazor.Core.sln b/src/dymaptic.GeoBlazor.Core.sln deleted file mode 100644 index fa5d43162..000000000 --- a/src/dymaptic.GeoBlazor.Core.sln +++ /dev/null @@ -1,310 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.0.11012.119 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CDCDDCD6-6958-4352-AF0C-C589ECD51C0D}" - ProjectSection(SolutionItems) = preProject - ..\Directory.Build.props = ..\Directory.Build.props - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dymaptic.GeoBlazor.Core", "dymaptic.GeoBlazor.Core\dymaptic.GeoBlazor.Core.csproj", "{92A18016-170A-42F3-B1F5-12CC306F796C}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dymaptic.GeoBlazor.Core.Sample.Shared", "..\samples\dymaptic.GeoBlazor.Core.Sample.Shared\dymaptic.GeoBlazor.Core.Sample.Shared.csproj", "{C993832C-734A-485F-A42B-30B620F32345}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dymaptic.GeoBlazor.Core.Sample.Wasm", "..\samples\dymaptic.GeoBlazor.Core.Sample.Wasm\dymaptic.GeoBlazor.Core.Sample.Wasm.csproj", "{79F14352-ACAD-4A53-84A2-C88237AD1497}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dymaptic.GeoBlazor.Core.Sample.Maui", "..\samples\dymaptic.GeoBlazor.Core.Sample.Maui\dymaptic.GeoBlazor.Core.Sample.Maui.csproj", "{08193B44-3A32-4836-96A0-44DEEA1E7556}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dymaptic.GeoBlazor.Core.Test.Unit", "..\test\dymaptic.GeoBlazor.Core.Test.Unit\dymaptic.GeoBlazor.Core.Test.Unit.csproj", "{48E045E3-F3FF-4D50-8987-83C1B8663E39}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dymaptic.GeoBlazor.Core.Test.Blazor.Shared", "..\test\dymaptic.GeoBlazor.Core.Test.Blazor.Shared\dymaptic.GeoBlazor.Core.Test.Blazor.Shared.csproj", "{41CD66DB-1487-4F37-B2C6-3DB569116E89}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.Sample.WebApp", "..\samples\dymaptic.GeoBlazor.Core.Sample.WebApp\dymaptic.GeoBlazor.Core.Sample.WebApp\dymaptic.GeoBlazor.Core.Sample.WebApp.csproj", "{E69BF0F0-C621-4484-89AB-62ADBD889B3D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.Sample.WebApp.Client", "..\samples\dymaptic.GeoBlazor.Core.Sample.WebApp\dymaptic.GeoBlazor.Core.Sample.WebApp.Client\dymaptic.GeoBlazor.Core.Sample.WebApp.Client.csproj", "{2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.Test.WebApp", "..\test\dymaptic.GeoBlazor.Core.Test.WebApp\dymaptic.GeoBlazor.Core.Test.WebApp\dymaptic.GeoBlazor.Core.Test.WebApp.csproj", "{056F7BB7-B92B-4616-8D29-F4EA50B47C4B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.Test.WebApp.Client", "..\test\dymaptic.GeoBlazor.Core.Test.WebApp\dymaptic.GeoBlazor.Core.Test.WebApp.Client\dymaptic.GeoBlazor.Core.Test.WebApp.Client.csproj", "{FE146E58-799C-47E8-A86A-AC19FB1C43D0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.Sample.OAuth", "..\samples\dymaptic.GeoBlazor.Core.Sample.OAuth\dymaptic.GeoBlazor.Core.Sample.OAuth\dymaptic.GeoBlazor.Core.Sample.OAuth.csproj", "{20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.Sample.OAuth.Client", "..\samples\dymaptic.GeoBlazor.Core.Sample.OAuth\dymaptic.GeoBlazor.Core.Sample.OAuth.Client\dymaptic.GeoBlazor.Core.Sample.OAuth.Client.csproj", "{9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.SourceGenerator", "dymaptic.GeoBlazor.Core.SourceGenerator\dymaptic.GeoBlazor.Core.SourceGenerator.csproj", "{1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.SourceGenerator.Tests", "..\test\dymaptic.GeoBlazor.Core.SourceGenerator.Tests\dymaptic.GeoBlazor.Core.SourceGenerator.Tests.csproj", "{4A58D766-A1EB-4A91-A316-E111F662F145}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.Sample.TokenRefresh", "..\samples\dymaptic.GeoBlazor.Core.Sample.TokenRefresh\dymaptic.GeoBlazor.Core.Sample.TokenRefresh\dymaptic.GeoBlazor.Core.Sample.TokenRefresh.csproj", "{7FA48135-81F7-1219-F001-A870BFC4224E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.Sample.TokenRefresh.Client", "..\samples\dymaptic.GeoBlazor.Core.Sample.TokenRefresh\dymaptic.GeoBlazor.Core.Sample.TokenRefresh.Client\dymaptic.GeoBlazor.Core.Sample.TokenRefresh.Client.csproj", "{6CB414BD-0536-A361-CEFF-C84EE3906931}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.Analyzers", "dymaptic.GeoBlazor.Core.Analyzers\dymaptic.GeoBlazor.Core.Analyzers.csproj", "{468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.SourceGenerator.Shared", "dymaptic.GeoBlazor.Core.SourceGenerator.Shared\dymaptic.GeoBlazor.Core.SourceGenerator.Shared.csproj", "{8FDFC824-2365-4467-9326-DFDFF6D6B775}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.Test.Automation", "..\test\dymaptic.GeoBlazor.Core.Test.Automation\dymaptic.GeoBlazor.Core.Test.Automation.csproj", "{679E2D83-C4D8-4350-83DC-9780364A0815}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dymaptic.GeoBlazor.Core.Test.Automation.SourceGeneration", "..\test\dymaptic.GeoBlazor.Core.Test.Automation.SourceGeneration\dymaptic.GeoBlazor.Core.Test.Automation.SourceGeneration.csproj", "{B70AE99D-782B-48E7-8713-DFAEB57809FF}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {92A18016-170A-42F3-B1F5-12CC306F796C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92A18016-170A-42F3-B1F5-12CC306F796C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92A18016-170A-42F3-B1F5-12CC306F796C}.Debug|x64.ActiveCfg = Debug|Any CPU - {92A18016-170A-42F3-B1F5-12CC306F796C}.Debug|x64.Build.0 = Debug|Any CPU - {92A18016-170A-42F3-B1F5-12CC306F796C}.Debug|x86.ActiveCfg = Debug|Any CPU - {92A18016-170A-42F3-B1F5-12CC306F796C}.Debug|x86.Build.0 = Debug|Any CPU - {92A18016-170A-42F3-B1F5-12CC306F796C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92A18016-170A-42F3-B1F5-12CC306F796C}.Release|Any CPU.Build.0 = Release|Any CPU - {92A18016-170A-42F3-B1F5-12CC306F796C}.Release|x64.ActiveCfg = Release|Any CPU - {92A18016-170A-42F3-B1F5-12CC306F796C}.Release|x64.Build.0 = Release|Any CPU - {92A18016-170A-42F3-B1F5-12CC306F796C}.Release|x86.ActiveCfg = Release|Any CPU - {92A18016-170A-42F3-B1F5-12CC306F796C}.Release|x86.Build.0 = Release|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Debug|x64.ActiveCfg = Debug|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Debug|x64.Build.0 = Debug|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Debug|x86.ActiveCfg = Debug|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Debug|x86.Build.0 = Debug|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Release|Any CPU.Build.0 = Release|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Release|x64.ActiveCfg = Release|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Release|x64.Build.0 = Release|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Release|x86.ActiveCfg = Release|Any CPU - {C993832C-734A-485F-A42B-30B620F32345}.Release|x86.Build.0 = Release|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Debug|Any CPU.Build.0 = Debug|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Debug|x64.ActiveCfg = Debug|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Debug|x64.Build.0 = Debug|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Debug|x86.ActiveCfg = Debug|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Debug|x86.Build.0 = Debug|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Release|Any CPU.ActiveCfg = Release|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Release|Any CPU.Build.0 = Release|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Release|x64.ActiveCfg = Release|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Release|x64.Build.0 = Release|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Release|x86.ActiveCfg = Release|Any CPU - {79F14352-ACAD-4A53-84A2-C88237AD1497}.Release|x86.Build.0 = Release|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Debug|x64.ActiveCfg = Debug|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Debug|x64.Build.0 = Debug|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Debug|x86.ActiveCfg = Debug|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Debug|x86.Build.0 = Debug|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Release|Any CPU.Build.0 = Release|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Release|Any CPU.Deploy.0 = Release|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Release|x64.ActiveCfg = Release|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Release|x64.Build.0 = Release|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Release|x86.ActiveCfg = Release|Any CPU - {08193B44-3A32-4836-96A0-44DEEA1E7556}.Release|x86.Build.0 = Release|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Debug|x64.ActiveCfg = Debug|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Debug|x64.Build.0 = Debug|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Debug|x86.ActiveCfg = Debug|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Debug|x86.Build.0 = Debug|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Release|Any CPU.ActiveCfg = Release|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Release|Any CPU.Build.0 = Release|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Release|x64.ActiveCfg = Release|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Release|x64.Build.0 = Release|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Release|x86.ActiveCfg = Release|Any CPU - {48E045E3-F3FF-4D50-8987-83C1B8663E39}.Release|x86.Build.0 = Release|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Debug|Any CPU.Build.0 = Debug|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Debug|x64.ActiveCfg = Debug|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Debug|x64.Build.0 = Debug|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Debug|x86.ActiveCfg = Debug|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Debug|x86.Build.0 = Debug|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Release|Any CPU.ActiveCfg = Release|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Release|Any CPU.Build.0 = Release|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Release|x64.ActiveCfg = Release|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Release|x64.Build.0 = Release|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Release|x86.ActiveCfg = Release|Any CPU - {41CD66DB-1487-4F37-B2C6-3DB569116E89}.Release|x86.Build.0 = Release|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Debug|x64.ActiveCfg = Debug|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Debug|x64.Build.0 = Debug|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Debug|x86.ActiveCfg = Debug|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Debug|x86.Build.0 = Debug|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Release|Any CPU.Build.0 = Release|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Release|x64.ActiveCfg = Release|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Release|x64.Build.0 = Release|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Release|x86.ActiveCfg = Release|Any CPU - {E69BF0F0-C621-4484-89AB-62ADBD889B3D}.Release|x86.Build.0 = Release|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Debug|x64.ActiveCfg = Debug|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Debug|x64.Build.0 = Debug|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Debug|x86.ActiveCfg = Debug|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Debug|x86.Build.0 = Debug|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Release|Any CPU.Build.0 = Release|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Release|x64.ActiveCfg = Release|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Release|x64.Build.0 = Release|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Release|x86.ActiveCfg = Release|Any CPU - {2A9CE42F-322C-4AAD-8FB8-0D586F5215E1}.Release|x86.Build.0 = Release|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Debug|x64.ActiveCfg = Debug|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Debug|x64.Build.0 = Debug|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Debug|x86.ActiveCfg = Debug|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Debug|x86.Build.0 = Debug|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Release|Any CPU.Build.0 = Release|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Release|x64.ActiveCfg = Release|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Release|x64.Build.0 = Release|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Release|x86.ActiveCfg = Release|Any CPU - {056F7BB7-B92B-4616-8D29-F4EA50B47C4B}.Release|x86.Build.0 = Release|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Debug|x64.ActiveCfg = Debug|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Debug|x64.Build.0 = Debug|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Debug|x86.ActiveCfg = Debug|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Debug|x86.Build.0 = Debug|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Release|Any CPU.Build.0 = Release|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Release|x64.ActiveCfg = Release|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Release|x64.Build.0 = Release|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Release|x86.ActiveCfg = Release|Any CPU - {FE146E58-799C-47E8-A86A-AC19FB1C43D0}.Release|x86.Build.0 = Release|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Debug|Any CPU.Build.0 = Debug|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Debug|x64.ActiveCfg = Debug|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Debug|x64.Build.0 = Debug|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Debug|x86.ActiveCfg = Debug|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Debug|x86.Build.0 = Debug|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Release|Any CPU.ActiveCfg = Release|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Release|Any CPU.Build.0 = Release|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Release|x64.ActiveCfg = Release|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Release|x64.Build.0 = Release|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Release|x86.ActiveCfg = Release|Any CPU - {20F1EBBE-A15F-40C0-A2E9-184CFDAD1326}.Release|x86.Build.0 = Release|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Debug|x64.ActiveCfg = Debug|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Debug|x64.Build.0 = Debug|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Debug|x86.ActiveCfg = Debug|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Debug|x86.Build.0 = Debug|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Release|Any CPU.Build.0 = Release|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Release|x64.ActiveCfg = Release|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Release|x64.Build.0 = Release|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Release|x86.ActiveCfg = Release|Any CPU - {9C60EDCD-CA86-452F-9E6F-CDBAF3604EAB}.Release|x86.Build.0 = Release|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Debug|x64.ActiveCfg = Debug|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Debug|x64.Build.0 = Debug|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Debug|x86.ActiveCfg = Debug|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Debug|x86.Build.0 = Debug|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Release|Any CPU.Build.0 = Release|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Release|x64.ActiveCfg = Release|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Release|x64.Build.0 = Release|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Release|x86.ActiveCfg = Release|Any CPU - {1E2AFAB9-32CD-4F79-A347-F29A0F1C7208}.Release|x86.Build.0 = Release|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Debug|x64.ActiveCfg = Debug|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Debug|x64.Build.0 = Debug|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Debug|x86.ActiveCfg = Debug|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Debug|x86.Build.0 = Debug|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Release|Any CPU.Build.0 = Release|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Release|x64.ActiveCfg = Release|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Release|x64.Build.0 = Release|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Release|x86.ActiveCfg = Release|Any CPU - {4A58D766-A1EB-4A91-A316-E111F662F145}.Release|x86.Build.0 = Release|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Debug|x64.ActiveCfg = Debug|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Debug|x64.Build.0 = Debug|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Debug|x86.ActiveCfg = Debug|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Debug|x86.Build.0 = Debug|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Release|Any CPU.Build.0 = Release|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Release|x64.ActiveCfg = Release|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Release|x64.Build.0 = Release|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Release|x86.ActiveCfg = Release|Any CPU - {7FA48135-81F7-1219-F001-A870BFC4224E}.Release|x86.Build.0 = Release|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Debug|x64.ActiveCfg = Debug|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Debug|x64.Build.0 = Debug|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Debug|x86.ActiveCfg = Debug|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Debug|x86.Build.0 = Debug|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Release|Any CPU.Build.0 = Release|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Release|x64.ActiveCfg = Release|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Release|x64.Build.0 = Release|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Release|x86.ActiveCfg = Release|Any CPU - {6CB414BD-0536-A361-CEFF-C84EE3906931}.Release|x86.Build.0 = Release|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Debug|Any CPU.Build.0 = Debug|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Debug|x64.ActiveCfg = Debug|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Debug|x64.Build.0 = Debug|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Debug|x86.ActiveCfg = Debug|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Debug|x86.Build.0 = Debug|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Release|Any CPU.ActiveCfg = Release|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Release|Any CPU.Build.0 = Release|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Release|x64.ActiveCfg = Release|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Release|x64.Build.0 = Release|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Release|x86.ActiveCfg = Release|Any CPU - {468F9CE4-A24F-4EE0-9C5B-2AF88A369C30}.Release|x86.Build.0 = Release|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Debug|Any CPU.Build.0 = Debug|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Debug|x64.ActiveCfg = Debug|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Debug|x64.Build.0 = Debug|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Debug|x86.ActiveCfg = Debug|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Debug|x86.Build.0 = Debug|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Release|Any CPU.ActiveCfg = Release|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Release|Any CPU.Build.0 = Release|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Release|x64.ActiveCfg = Release|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Release|x64.Build.0 = Release|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Release|x86.ActiveCfg = Release|Any CPU - {679E2D83-C4D8-4350-83DC-9780364A0815}.Release|x86.Build.0 = Release|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Debug|x64.ActiveCfg = Debug|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Debug|x64.Build.0 = Debug|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Debug|x86.ActiveCfg = Debug|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Debug|x86.Build.0 = Debug|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Release|Any CPU.Build.0 = Release|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Release|x64.ActiveCfg = Release|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Release|x64.Build.0 = Release|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Release|x86.ActiveCfg = Release|Any CPU - {B70AE99D-782B-48E7-8713-DFAEB57809FF}.Release|x86.Build.0 = Release|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Debug|x64.ActiveCfg = Debug|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Debug|x64.Build.0 = Debug|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Debug|x86.ActiveCfg = Debug|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Debug|x86.Build.0 = Debug|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Release|Any CPU.Build.0 = Release|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Release|x64.ActiveCfg = Release|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Release|x64.Build.0 = Release|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Release|x86.ActiveCfg = Release|Any CPU - {8FDFC824-2365-4467-9326-DFDFF6D6B775}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {FA50C146-4061-45AA-A5B3-5DE48D47FF88} - EndGlobalSection -EndGlobal diff --git a/src/dymaptic.GeoBlazor.Core/Components/ActionBase.cs b/src/dymaptic.GeoBlazor.Core/Components/ActionBase.cs index 8bdff5798..4c875e679 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/ActionBase.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/ActionBase.cs @@ -18,7 +18,7 @@ public abstract partial class ActionBase : MapComponent, IProtobufSerializable /// This adds a CSS class to the ActionButton's node. /// @@ -39,12 +39,20 @@ public abstract partial class ActionBase : MapComponent, IProtobufSerializable? CallbackFunction { get; set; } - + /// /// Identifies whether the action has a callback function. /// public bool HasCallbackFunction => CallbackFunction != null; - + + /// + /// Specifies the type of action. Choose between "button" or "toggle". + /// + public abstract string Type { get; } + + /// + public abstract ActionBaseSerializationRecord ToProtobuf(); + /// /// JS-invokable method for triggering actions. /// @@ -60,14 +68,6 @@ public async Task OnJsTriggerAction(PopupTriggerActionEvent triggerActionEvent) await CallbackFunction!.Invoke(); } } - - /// - /// Specifies the type of action. Choose between "button" or "toggle". - /// - public abstract string Type { get; } - - /// - public abstract ActionBaseSerializationRecord ToProtobuf(); } internal class ActionBaseConverter : JsonConverter diff --git a/src/dymaptic.GeoBlazor.Core/Components/ColorVariable.gb.cs b/src/dymaptic.GeoBlazor.Core/Components/ColorVariable.gb.cs index 7006d1b43..c4af2f7f0 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/ColorVariable.gb.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/ColorVariable.gb.cs @@ -50,7 +50,7 @@ public ColorVariable() /// Arcade expression as defined in the valueExpression property. /// ArcGIS Maps SDK for JavaScript /// - public ColorVariable(string field, + public ColorVariable(string? field, string? normalizationField = null, IReadOnlyList? stops = null, VisualVariableLegendOptions? legendOptions = null, diff --git a/src/dymaptic.GeoBlazor.Core/Components/DynamicLayer.cs b/src/dymaptic.GeoBlazor.Core/Components/DynamicLayer.cs index 6f91f3c7a..23cf386cf 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/DynamicLayer.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/DynamicLayer.cs @@ -12,7 +12,6 @@ public abstract class DynamicLayer : MapComponent public abstract string Type { get; } } - /// /// Abstract base class for data sources in a dynamic data layer. /// @@ -25,7 +24,6 @@ public abstract class DynamicDataSource : MapComponent public abstract DynamicDataSourceType Type { get; } } - internal class DynamicLayerConverter : JsonConverter { public override DynamicLayer? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) @@ -64,7 +62,8 @@ public override void Write(Utf8JsonWriter writer, DynamicLayer value, JsonSerial internal class DynamicDataSourceConverter : JsonConverter { - public override DynamicDataSource? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override DynamicDataSource? Read(ref Utf8JsonReader reader, Type typeToConvert, + JsonSerializerOptions options) { var newOptions = new JsonSerializerOptions(options) { diff --git a/src/dymaptic.GeoBlazor.Core/Components/Geometries/Point.cs b/src/dymaptic.GeoBlazor.Core/Components/Geometries/Point.cs index 3d5870811..3dd143115 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/Geometries/Point.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/Geometries/Point.cs @@ -3,6 +3,94 @@ namespace dymaptic.GeoBlazor.Core.Components.Geometries; [ProtobufSerializable] public partial class Point : Geometry { + /// + /// Parameterless constructor for use as a Razor Component. + /// + [ActivatorUtilitiesConstructor] + [CodeGenerationIgnore] + public Point() + { + } + + /// + /// Constructor for use in C# code. Use named parameters (e.g., item1: value1, item2: value2) to set properties in any order. + /// + /// + /// The longitude of the point. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The latitude of the point. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The x-coordinate (easting) of the point in map units. + /// default 0 + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The y-coordinate (northing) of the point in map units. + /// default 0 + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The z-coordinate (or elevation) of the point in map units. + /// default undefined + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The spatial reference of the geometry. + /// default SpatialReference.WGS84 // wkid: 4326 + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// Indicates if the geometry has M values. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// Indicates if the geometry has z-values (elevation). + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The m-coordinate of the point in map units. + /// default undefined + /// ArcGIS Maps SDK for JavaScript + /// + [CodeGenerationIgnore] + public Point(double? longitude = null, + double? latitude = null, + double? x = null, + double? y = null, + double? z = null, + SpatialReference? spatialReference = null, + bool? hasM = null, + bool? hasZ = null, + double? m = null) + { + AllowRender = false; + + if (x is null && longitude is null || y is null && latitude is null) + { + throw new ArgumentException("Points must have X and Y coordinates or longitude and latitude."); + } + + if (x is null && y is null && spatialReference is { Wkid: not 4326 }) + { + } + +#pragma warning disable BL0005 + Longitude = longitude; + Latitude = latitude; + X = x; + Y = y; + Z = z; + SpatialReference = spatialReference; + HasM = hasM; + HasZ = hasZ; + M = m; +#pragma warning restore BL0005 + } + /// /// The latitude of the point. /// @@ -66,7 +154,7 @@ public Point Clone() /// public override GeometrySerializationRecord ToProtobuf() { - return new GeometrySerializationRecord(Id.ToString(), Type.ToString().ToKebabCase(), + return new GeometrySerializationRecord(Id.ToString(), Type.ToString().ToKebabCase(), Extent?.ToProtobuf(), SpatialReference?.ToProtobuf()) { diff --git a/src/dymaptic.GeoBlazor.Core/Components/Geometries/Point.gb.cs b/src/dymaptic.GeoBlazor.Core/Components/Geometries/Point.gb.cs index 1c3ffd2e5..accd60cd0 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/Geometries/Point.gb.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/Geometries/Point.gb.cs @@ -2,7 +2,6 @@ namespace dymaptic.GeoBlazor.Core.Components.Geometries; - /// /// GeoBlazor Docs /// A location defined by X, Y, and Z coordinates. @@ -10,85 +9,6 @@ namespace dymaptic.GeoBlazor.Core.Components.Geometries; /// public partial class Point { - - /// - /// Parameterless constructor for use as a Razor Component. - /// - [ActivatorUtilitiesConstructor] - public Point() - { - } - - /// - /// Constructor for use in C# code. Use named parameters (e.g., item1: value1, item2: value2) to set properties in any order. - /// - /// - /// The longitude of the point. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The latitude of the point. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The x-coordinate (easting) of the point in map units. - /// default 0 - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The y-coordinate (northing) of the point in map units. - /// default 0 - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The z-coordinate (or elevation) of the point in map units. - /// default undefined - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The spatial reference of the geometry. - /// default SpatialReference.WGS84 // wkid: 4326 - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// Indicates if the geometry has M values. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// Indicates if the geometry has z-values (elevation). - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The m-coordinate of the point in map units. - /// default undefined - /// ArcGIS Maps SDK for JavaScript - /// - public Point( - double? longitude = null, - double? latitude = null, - double? x = null, - double? y = null, - double? z = null, - SpatialReference? spatialReference = null, - bool? hasM = null, - bool? hasZ = null, - double? m = null) - { - AllowRender = false; -#pragma warning disable BL0005 - Longitude = longitude; - Latitude = latitude; - X = x; - Y = y; - Z = z; - SpatialReference = spatialReference; - HasM = hasM; - HasZ = hasZ; - M = m; -#pragma warning restore BL0005 - } - - #region Property Getters /// @@ -100,8 +20,8 @@ public Point( { return Latitude; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -110,26 +30,28 @@ public Point( { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return Latitude; } // get the property value - JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync("getNullableValueTypedProperty", + JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync( + "getNullableValueTypedProperty", CancellationTokenSource.Token, JsComponentReference, "latitude"); + if (result is { Value: not null }) { #pragma warning disable BL0005 - Latitude = result.Value.Value; + Latitude = result.Value.Value; #pragma warning restore BL0005 - ModifiedParameters[nameof(Latitude)] = Latitude; + ModifiedParameters[nameof(Latitude)] = Latitude; } - + return Latitude; } - + /// /// Asynchronously retrieve the current value of the Longitude property. /// @@ -139,8 +61,8 @@ public Point( { return Longitude; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -149,26 +71,28 @@ public Point( { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return Longitude; } // get the property value - JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync("getNullableValueTypedProperty", + JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync( + "getNullableValueTypedProperty", CancellationTokenSource.Token, JsComponentReference, "longitude"); + if (result is { Value: not null }) { #pragma warning disable BL0005 - Longitude = result.Value.Value; + Longitude = result.Value.Value; #pragma warning restore BL0005 - ModifiedParameters[nameof(Longitude)] = Longitude; + ModifiedParameters[nameof(Longitude)] = Longitude; } - + return Longitude; } - + /// /// Asynchronously retrieve the current value of the M property. /// @@ -178,8 +102,8 @@ public Point( { return M; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -188,26 +112,28 @@ public Point( { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return M; } // get the property value - JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync("getNullableValueTypedProperty", + JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync( + "getNullableValueTypedProperty", CancellationTokenSource.Token, JsComponentReference, "m"); + if (result is { Value: not null }) { #pragma warning disable BL0005 - M = result.Value.Value; + M = result.Value.Value; #pragma warning restore BL0005 - ModifiedParameters[nameof(M)] = M; + ModifiedParameters[nameof(M)] = M; } - + return M; } - + /// /// Asynchronously retrieve the current value of the X property. /// @@ -217,8 +143,8 @@ public Point( { return X; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -227,26 +153,28 @@ public Point( { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return X; } // get the property value - JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync("getNullableValueTypedProperty", + JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync( + "getNullableValueTypedProperty", CancellationTokenSource.Token, JsComponentReference, "x"); + if (result is { Value: not null }) { #pragma warning disable BL0005 - X = result.Value.Value; + X = result.Value.Value; #pragma warning restore BL0005 - ModifiedParameters[nameof(X)] = X; + ModifiedParameters[nameof(X)] = X; } - + return X; } - + /// /// Asynchronously retrieve the current value of the Y property. /// @@ -256,8 +184,8 @@ public Point( { return Y; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -266,26 +194,28 @@ public Point( { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return Y; } // get the property value - JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync("getNullableValueTypedProperty", + JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync( + "getNullableValueTypedProperty", CancellationTokenSource.Token, JsComponentReference, "y"); + if (result is { Value: not null }) { #pragma warning disable BL0005 - Y = result.Value.Value; + Y = result.Value.Value; #pragma warning restore BL0005 - ModifiedParameters[nameof(Y)] = Y; + ModifiedParameters[nameof(Y)] = Y; } - + return Y; } - + /// /// Asynchronously retrieve the current value of the Z property. /// @@ -295,8 +225,8 @@ public Point( { return Z; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -305,28 +235,31 @@ public Point( { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return Z; } // get the property value - JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync("getNullableValueTypedProperty", + JsNullableDoubleWrapper? result = await CoreJsModule!.InvokeAsync( + "getNullableValueTypedProperty", CancellationTokenSource.Token, JsComponentReference, "z"); + if (result is { Value: not null }) { #pragma warning disable BL0005 - Z = result.Value.Value; + Z = result.Value.Value; #pragma warning restore BL0005 - ModifiedParameters[nameof(Z)] = Z; + ModifiedParameters[nameof(Z)] = Z; } - + return Z; } - + #endregion + #region Property Setters /// @@ -341,13 +274,13 @@ public async Task SetLatitude(double? value) Latitude = value; #pragma warning restore BL0005 ModifiedParameters[nameof(Latitude)] = value; - + if (CoreJsModule is null) { return; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -356,16 +289,16 @@ public async Task SetLatitude(double? value) { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return; } - + await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, JsComponentReference, "latitude", value); } - + /// /// Asynchronously set the value of the Longitude property after render. /// @@ -378,13 +311,13 @@ public async Task SetLongitude(double? value) Longitude = value; #pragma warning restore BL0005 ModifiedParameters[nameof(Longitude)] = value; - + if (CoreJsModule is null) { return; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -393,16 +326,16 @@ public async Task SetLongitude(double? value) { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return; } - + await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, JsComponentReference, "longitude", value); } - + /// /// Asynchronously set the value of the M property after render. /// @@ -415,13 +348,13 @@ public async Task SetM(double? value) M = value; #pragma warning restore BL0005 ModifiedParameters[nameof(M)] = value; - + if (CoreJsModule is null) { return; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -430,16 +363,16 @@ public async Task SetM(double? value) { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return; } - + await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, JsComponentReference, "m", value); } - + /// /// Asynchronously set the value of the X property after render. /// @@ -452,13 +385,13 @@ public async Task SetX(double? value) X = value; #pragma warning restore BL0005 ModifiedParameters[nameof(X)] = value; - + if (CoreJsModule is null) { return; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -467,16 +400,16 @@ public async Task SetX(double? value) { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return; } - + await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, JsComponentReference, "x", value); } - + /// /// Asynchronously set the value of the Y property after render. /// @@ -489,13 +422,13 @@ public async Task SetY(double? value) Y = value; #pragma warning restore BL0005 ModifiedParameters[nameof(Y)] = value; - + if (CoreJsModule is null) { return; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -504,16 +437,16 @@ public async Task SetY(double? value) { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return; } - + await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, JsComponentReference, "y", value); } - + /// /// Asynchronously set the value of the Z property after render. /// @@ -526,13 +459,13 @@ public async Task SetZ(double? value) Z = value; #pragma warning restore BL0005 ModifiedParameters[nameof(Z)] = value; - + if (CoreJsModule is null) { return; } - - try + + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( "getJsComponent", CancellationTokenSource.Token, Id); @@ -541,18 +474,19 @@ public async Task SetZ(double? value) { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return; } - + await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, JsComponentReference, "z", value); } - + #endregion + #region Public Methods /// @@ -570,7 +504,7 @@ await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, { return null; } - + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( @@ -580,18 +514,17 @@ await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return null; } - - return await JsComponentReference!.InvokeAsync( - "copy", + + return await JsComponentReference!.InvokeAsync("copy", CancellationTokenSource.Token, other); } - + /// /// GeoBlazor Docs /// Computes the Euclidean distance between this Point and a given Point. @@ -607,7 +540,7 @@ await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, { return null; } - + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( @@ -617,18 +550,17 @@ await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return null; } - - return await JsComponentReference!.InvokeAsync( - "distance", + + return await JsComponentReference!.InvokeAsync("distance", CancellationTokenSource.Token, other); } - + /// /// GeoBlazor Docs /// Modifies the point geometry in-place by shifting the X-coordinate to within @@ -642,7 +574,7 @@ await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, { return null; } - + try { JsComponentReference ??= await CoreJsModule.InvokeAsync( @@ -652,17 +584,15 @@ await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, { // this is expected if the component is not yet built } - + if (JsComponentReference is null) { return null; } - - return await JsComponentReference!.InvokeAsync( - "normalize", + + return await JsComponentReference!.InvokeAsync("normalize", CancellationTokenSource.Token); } - -#endregion -} +#endregion +} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Components/GeometryEngine.cs b/src/dymaptic.GeoBlazor.Core/Components/GeometryEngine.cs deleted file mode 100644 index 417db6d05..000000000 --- a/src/dymaptic.GeoBlazor.Core/Components/GeometryEngine.cs +++ /dev/null @@ -1,1730 +0,0 @@ -// ReSharper disable RedundantEnumerableCastCall -namespace dymaptic.GeoBlazor.Core.Components; -/// -/// A client-side geometry engine for testing, measuring, and analyzing the spatial relationship between two or more 2D geometries. If more than one geometry is required for any of the methods below, all geometries must have the same spatial reference for the methods to work as expected. -/// ArcGIS Maps SDK for JavaScript -/// -[CodeGenerationIgnore] -public class GeometryEngine : LogicComponent -{ - /// - /// Default Constructor - /// - /// - /// Injected Identity Manager reference - /// - public GeometryEngine(AuthenticationManager authenticationManager) : base(authenticationManager) - { - } - - /// - protected override string ComponentName => nameof(GeometryEngine); - - /// - /// Creates planar (or Euclidean) buffer polygons at a specified distance around the input geometries. - /// - /// - /// The GeometryEngine has two methods for buffering geometries client-side: buffer and geodesicBuffer. Use caution when deciding which method to use. As a general rule, use geodesicBuffer if the input geometries have a spatial - /// reference of either WGS84 (wkid: 4326) or Web Mercator. Only use buffer (this method) when attempting to buffer - /// geometries with a projected coordinate system other than Web Mercator. If you need to buffer geometries with a - /// geographic coordinate system other than WGS84 (wkid: 4326), use geometryService.buffer(). - /// - /// - /// The buffer input geometries. - /// - /// - /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The value of the geometry array will be matched one to one with those in the distance array until the final value of the distance array is reached, in which case that value will be applied to the remaining geometries. - /// - /// - /// The resulting buffers. - /// - public async Task Buffer(IEnumerable geometries, IEnumerable distances) - { - return await InvokeAsync("buffer", geometries, distances, null, null); - } - - /// - /// Creates planar (or Euclidean) buffer polygons at a specified distance around the input geometries. - /// - /// - /// The GeometryEngine has two methods for buffering geometries client-side: buffer and geodesicBuffer. Use caution when deciding which method to use. As a general rule, use geodesicBuffer if the input geometries have a spatial - /// reference of either WGS84 (wkid: 4326) or Web Mercator. Only use buffer (this method) when attempting to buffer - /// geometries with a projected coordinate system other than Web Mercator. If you need to buffer geometries with a - /// geographic coordinate system other than WGS84 (wkid: 4326), use geometryService.buffer(). - /// - /// - /// The buffer input geometries. - /// - /// - /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The value of the geometry array will be matched one to one with those in the distance array until the final value of the distance array is reached, in which case that value will be applied to the remaining geometries. - /// - /// - /// Measurement unit of the distance(s). Defaults to the units of the input geometries. - /// - /// - /// The resulting buffers. - /// - public async Task Buffer(IEnumerable geometries, IEnumerable distances, - GeometryEngineLinearUnit? unit) - { - return await InvokeAsync("buffer", geometries, distances, unit, null); - } - - /// - /// Creates planar (or Euclidean) buffer polygons at a specified distance around the input geometries. - /// - /// - /// The GeometryEngine has two methods for buffering geometries client-side: buffer and geodesicBuffer. Use caution when deciding which method to use. As a general rule, use geodesicBuffer if the input geometries have a spatial - /// reference of either WGS84 (wkid: 4326) or Web Mercator. Only use buffer (this method) when attempting to buffer - /// geometries with a projected coordinate system other than Web Mercator. If you need to buffer geometries with a - /// geographic coordinate system other than WGS84 (wkid: 4326), use geometryService.buffer(). - /// - /// - /// The buffer input geometries. - /// - /// - /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The value of the geometry array will be matched one to one with those in the distance array until the final value of the distance array is reached, in which case that value will be applied to the remaining geometries. - /// - /// - /// Measurement unit of the distance(s). Defaults to the units of the input geometries. - /// - /// - /// Determines whether the output geometries should be unioned into a single polygon. - /// - /// - /// The resulting buffers. - /// - public async Task Buffer(IEnumerable geometries, IEnumerable distances, - GeometryEngineLinearUnit? unit, bool? unionResults) - { - return await InvokeAsync("buffer", geometries, distances, unit, unionResults); - } - - /// - /// Creates planar (or Euclidean) buffer polygons at a specified distance around the input geometries. - /// - /// - /// The GeometryEngine has two methods for buffering geometries client-side: buffer and geodesicBuffer. Use caution when deciding which method to use. As a general rule, use geodesicBuffer if the input geometries have a spatial - /// reference of either WGS84 (wkid: 4326) or Web Mercator. Only use buffer (this method) when attempting to buffer - /// geometries with a projected coordinate system other than Web Mercator. If you need to buffer geometries with a - /// geographic coordinate system other than WGS84 (wkid: 4326), use geometryService.buffer(). - /// - /// - /// The buffer input geometries. - /// - /// - /// The specified distance(s) for buffering. - /// - /// - /// The resulting buffer. - /// - public async Task Buffer(Geometry geometry, double distance) - { - return await InvokeAsync("buffer", geometry, distance, null); - } - - /// - /// Creates planar (or Euclidean) buffer polygons at a specified distance around the input geometries. - /// - /// - /// The GeometryEngine has two methods for buffering geometries client-side: buffer and geodesicBuffer. Use caution when deciding which method to use. As a general rule, use geodesicBuffer if the input geometries have a spatial - /// reference of either WGS84 (wkid: 4326) or Web Mercator. Only use buffer (this method) when attempting to buffer - /// geometries with a projected coordinate system other than Web Mercator. If you need to buffer geometries with a - /// geographic coordinate system other than WGS84 (wkid: 4326), use geometryService.buffer(). - /// - /// - /// The buffer input geometries. - /// - /// - /// The specified distance(s) for buffering. - /// - /// - /// Measurement unit of the distance(s). Defaults to the units of the input geometries. - /// - /// - /// The resulting buffer. - /// - public async Task Buffer(Geometry geometry, double distance, GeometryEngineLinearUnit? unit) - { - return await InvokeAsync("buffer", geometry, distance, unit); - } - - /// - /// Calculates the clipped geometry from a target geometry by an envelope. - /// - /// - /// The geometry to be clipped. - /// - /// - /// The envelope used to clip. - /// - /// - /// Clipped geometry. - /// - public async Task Clip(Geometry geometry, Extent extent) - { - return await InvokeAsync("clip", geometry, extent); - } - - /// - /// Indicates if one geometry contains another geometry. - /// - /// - /// The geometry that is tested for the "contains" relationship to the other geometry. Think of this geometry as the potential "container" of the insideGeometry. - /// - /// - /// The geometry that is tested for the "within" relationship to the containerGeometry. - /// - /// - /// Returns true if the containerGeometry contains the insideGeometry. - /// - public async Task Contains(Geometry containerGeometry, Geometry insideGeometry) - { - return await InvokeAsync("contains", containerGeometry, insideGeometry); - } - - /// - /// Calculates the convex hull of one or more geometries. A convex hull is the smallest convex polygon that encloses a group of geometries or vertices. The input can be a single geometry (such as a polyline) or an array of any geometry type. The hull is typically a polygon but can also be a polyline or a point in degenerate cases. - /// - /// - /// The input geometries used to calculate the convex hull. The input array can include various geometry types. - /// - /// - /// Indicates whether to merge the output into a single geometry (usually a polygon). - /// - /// - /// Returns the convex hull of the input geometries. This is usually a polygon, but can also be a polyline (if the - /// input is a set of points or polylines forming a straight line), or a point (in degenerate cases). - /// - public async Task ConvexHull(IEnumerable geometries, bool? merge = null) - { - return await InvokeAsync("convexHull", geometries, merge); - } - - /// - /// Calculates the convex hull of one or more geometries. A convex hull is the smallest convex polygon that encloses a group of geometries or vertices. The input can be a single geometry (such as a polyline) or an array of any geometry type. The hull is typically a polygon but can also be a polyline or a point in degenerate cases. - /// - /// - /// The input geometry used to calculate the convex hull. - /// - /// - /// Returns the convex hull of the input geometries. This is usually a polygon, but can also be a polyline (if the - /// input is a set of points or polylines forming a straight line), or a point (in degenerate cases). - /// - public async Task ConvexHull(Geometry geometry) - { - return await InvokeAsync("convexHull", geometry); - } - - /// - /// Indicates if one geometry crosses another geometry. - /// - /// - /// The geometry to cross. - /// - /// - /// The geometry being crossed. - /// - /// - /// Returns true if geometry1 crosses geometry2. - /// - public async Task Crosses(Geometry geometry1, Geometry geometry2) - { - return await InvokeAsync("crosses", geometry1, geometry2); - } - - /// - /// Splits the input Polyline or Polygon where it crosses a cutting Polyline. For Polylines, all left cuts are grouped together in the first Geometry. Right cuts and coincident cuts are grouped in the second Geometry and each undefined cut, along with any uncut parts, are output as separate Polylines. For Polygons, all left cuts are grouped in the first Polygon, all right cuts are grouped in the second Polygon, and each undefined cut, along with any leftover parts after cutting, are output as a separate Polygon. If no cuts are returned then the array will be empty. An undefined cut will only be produced if a left cut or right cut was produced and there was a part left over after cutting, or a cut is bounded to the left and right of the cutter. - /// - /// - /// The geometry to be cut. - /// - /// - /// The polyline to cut the geometry. - /// - /// - /// Returns an array of geometries created by cutting the input geometry with the cutter. - /// - public async Task Cut(Geometry geometry, Polyline cutter) - { - return await InvokeAsync("cut", geometry, cutter); - } - - /// - /// Densify geometries by plotting points between existing vertices. - /// - /// - /// The geometry to be densified. - /// - /// - /// The maximum segment length allowed. Must be a positive value. - /// - /// - /// Measurement unit for maxSegmentLength. Defaults to the units of the input geometry. - /// - /// - /// The densified geometry. - /// - public async Task Densify(Geometry geometry, double maxSegmentLength, GeometryEngineLinearUnit? maxSegmentLengthUnit = null) - { - return await InvokeAsync("densify", geometry, maxSegmentLength, maxSegmentLengthUnit); - } - - /// - /// Creates the difference of two geometries. The resultant geometry is the portion of inputGeometry not in the subtractor. The dimension of the subtractor has to be equal to or greater than that of the inputGeometry. - /// - /// - /// The input geometries to subtract from. - /// - /// - /// The geometry being subtracted from inputGeometry. - /// - /// - /// Returns the geometry of inputGeometry minus the subtractor geometry. - /// - public async Task Difference(IEnumerable geometries, Geometry subtractor) - { - return await InvokeAsync("difference", geometries, subtractor); - } - - /// - /// Creates the difference of two geometries. The resultant geometry is the portion of inputGeometry not in the subtractor. The dimension of the subtractor has to be equal to or greater than that of the inputGeometry. - /// - /// - /// The input geometry to subtract from. - /// - /// - /// The geometry being subtracted from inputGeometry. - /// - /// - /// Returns the geometry of inputGeometry minus the subtractor geometry. - /// - public async Task Difference(Geometry geometry, Geometry subtractor) - { - return await InvokeAsync("difference", geometry, subtractor); - } - - /// - /// Indicates if one geometry is disjoint (doesn't intersect in any way) with another geometry. - /// - /// - /// The base geometry that is tested for the "disjoint" relationship to the other geometry. - /// - /// - /// The comparison geometry that is tested for the "disjoint" relationship to the other geometry. - /// - /// - /// Returns true if geometry1 and geometry2 are disjoint (don't intersect in any way). - /// - public async Task Disjoint(Geometry geometry1, Geometry geometry2) - { - return await InvokeAsync("disjoint", geometry1, geometry2); - } - - /// - /// Calculates the shortest planar distance between two geometries. Distance is reported in the linear units specified by distanceUnit or, if distanceUnit is null, the units of the spatialReference of input geometry. - /// - /// - /// First input geometry. - /// - /// - /// Second input geometry. - /// - /// - /// Measurement unit of the return value. Defaults to the units of the input geometries. - /// - /// - /// Distance between the two input geometries. - /// - public async Task Distance(Geometry geometry1, Geometry geometry2, GeometryEngineLinearUnit? distanceUnit = null) - { - return await InvokeAsync("distance", geometry1, geometry2, distanceUnit); - } - - /// - /// Indicates if two geometries are equal. - /// - /// - /// In ArcGIS for JS, this method is called `Equals`. However, this term has special meaning in .NET, so we have renamed here. - /// - /// - /// First input geometry. - /// - /// - /// Second input geometry. - /// - /// - /// Returns true if the two input geometries are equal. - /// - public async Task AreEqual(Geometry geometry1, Geometry geometry2) - { - return await InvokeAsync("equals", geometry1, geometry2); - } - - /// - /// Returns an object containing additional information about the input spatial reference. - /// - /// - /// The input spatial reference. - /// - /// - /// Resolves to a object. - /// - public async Task ExtendedSpatialReferenceInfo(SpatialReference spatialReference) - { - return await InvokeAsync("extendedSpatialReferenceInfo", spatialReference); - } - - /// - /// Flips a geometry on the horizontal axis. Can optionally be flipped around a point. - /// - /// - /// The input geometry to be flipped. - /// - /// - /// Point to flip the geometry around. Defaults to the centroid of the geometry. - /// - /// - /// The flipped geometry. - /// - public async Task FlipHorizontal(Geometry geometry, Point? flipOrigin = null) - { - return await InvokeAsync("flipHorizontal", geometry, flipOrigin); - } - - /// - /// Flips a geometry on the vertical axis. Can optionally be flipped around a point. - /// - /// - /// The input geometry to be flipped. - /// - /// - /// Point to flip the geometry around. Defaults to the centroid of the geometry. - /// - /// - /// The flipped geometry. - /// - public async Task FlipVertical(Geometry geometry, Point? flipOrigin = null) - { - return await InvokeAsync("flipVertical", geometry, flipOrigin); - } - - /// - /// Performs the generalize operation on the geometries in the cursor. Point and Multipoint geometries are left unchanged. Envelope is converted to a Polygon and then generalized. - /// - /// - /// The input geometry to be generalized. - /// - /// - /// The maximum allowed deviation from the generalized geometry to the original geometry. - /// - /// - /// When true the degenerate parts of the geometry will be removed from the output (may be undesired for drawing). - /// - /// - /// Measurement unit for maxDeviation. Defaults to the units of the input geometry. - /// - /// - /// The generalized geometry. - /// - public async Task Generalize(Geometry geometry, double maxDeviation, bool? removeDegenerateParts = null, GeometryEngineLinearUnit? maxDeviationUnit = null) - { - return await InvokeAsync("generalize", geometry, maxDeviation, removeDegenerateParts, maxDeviationUnit); - } - - /// - /// Calculates the area of the input geometry. As opposed to planarArea(), geodesicArea takes into account the curvature of the earth when performing this calculation. Therefore, when using input geometries with a spatial reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to calculate areas using geodesicArea(). If the input geometries have a projected coordinate system other than Web Mercator, use planarArea() instead. - /// - /// - /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. - /// - /// - /// The input polygon - /// - /// - /// Measurement unit of the return value. Defaults to the units of the input geometries. - /// - /// - /// Area of the input geometry. - /// - public async Task GeodesicArea(Polygon geometry, GeometryEngineAreaUnit? unit = null) - { - return await InvokeAsync("geodesicArea", geometry, unit); - } - - /// - /// Creates geodesic buffer polygons at a specified distance around the input geometries. When calculating distances, this method takes the curvature of the earth into account, which provides highly accurate results when dealing with very large geometries and/or geometries that spatially vary on a global scale where one projected coordinate system could not accurately plot coordinates and measure distances for all the geometries. - /// - /// - /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. In general, if your input geometries are assigned one of those two spatial references, you should always use geodesicBuffer() to obtain the - /// most accurate results for those geometries. If needing to buffer points assigned a projected coordinate system - /// other than Web Mercator, use buffer() instead. If the input geometries have a geographic coordinate system other - /// than WGS84 (wkid: 4326), use geometryService.buffer(). - /// - /// - /// The buffer input geometries - /// - /// - /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The value of the geometry array will be matched one to one with those in the distance array until the final value of the distance array is reached, in which case that value will be applied to the remaining geometries. - /// - /// - /// The resulting buffers - /// - public async Task GeodesicBuffer(IEnumerable geometries, IEnumerable distances) - { - return await InvokeAsync("geodesicBuffer", geometries, distances, null, null); - } - - /// - /// Creates geodesic buffer polygons at a specified distance around the input geometries. When calculating distances, this method takes the curvature of the earth into account, which provides highly accurate results when dealing with very large geometries and/or geometries that spatially vary on a global scale where one projected coordinate system could not accurately plot coordinates and measure distances for all the geometries. - /// - /// - /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. In general, if your input geometries are assigned one of those two spatial references, you should always use geodesicBuffer() to obtain the - /// most accurate results for those geometries. If needing to buffer points assigned a projected coordinate system - /// other than Web Mercator, use buffer() instead. If the input geometries have a geographic coordinate system other - /// than WGS84 (wkid: 4326), use geometryService.buffer(). - /// - /// - /// The buffer input geometries - /// - /// - /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The value of the geometry array will be matched one to one with those in the distance array until the final value of the distance array is reached, in which case that value will be applied to the remaining geometries. - /// - /// - /// Measurement unit of the distance(s). Defaults to the units of the input geometries. - /// - /// - /// The resulting buffers - /// - public async Task GeodesicBuffer(IEnumerable geometries, IEnumerable distances, - GeometryEngineLinearUnit? unit) - { - return await InvokeAsync("geodesicBuffer", geometries, distances, unit, null); - } - - /// - /// Creates geodesic buffer polygons at a specified distance around the input geometries. When calculating distances, this method takes the curvature of the earth into account, which provides highly accurate results when dealing with very large geometries and/or geometries that spatially vary on a global scale where one projected coordinate system could not accurately plot coordinates and measure distances for all the geometries. - /// - /// - /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. In general, if your input geometries are assigned one of those two spatial references, you should always use geodesicBuffer() to obtain the - /// most accurate results for those geometries. If needing to buffer points assigned a projected coordinate system - /// other than Web Mercator, use buffer() instead. If the input geometries have a geographic coordinate system other - /// than WGS84 (wkid: 4326), use geometryService.buffer(). - /// - /// - /// The buffer input geometries - /// - /// - /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The value of the geometry array will be matched one to one with those in the distance array until the final value of the distance array is reached, in which case that value will be applied to the remaining geometries. - /// - /// - /// Measurement unit of the distance(s). Defaults to the units of the input geometries. - /// - /// - /// Determines whether the output geometries should be unioned into a single polygon. - /// - /// - /// The resulting buffers - /// - public async Task GeodesicBuffer(IEnumerable geometries, IEnumerable distances, - GeometryEngineLinearUnit? unit, bool? unionResults) - { - return await InvokeAsync("geodesicBuffer", geometries, distances, unit, unionResults); - } - - /// - /// Creates geodesic buffer polygons at a specified distance around the input geometries. When calculating distances, this method takes the curvature of the earth into account, which provides highly accurate results when dealing with very large geometries and/or geometries that spatially vary on a global scale where one projected coordinate system could not accurately plot coordinates and measure distances for all the geometries. - /// - /// - /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. In general, if your input geometries are assigned one of those two spatial references, you should always use geodesicBuffer() to obtain the - /// most accurate results for those geometries. If needing to buffer points assigned a projected coordinate system - /// other than Web Mercator, use buffer() instead. If the input geometries have a geographic coordinate system other - /// than WGS84 (wkid: 4326), use geometryService.buffer(). - /// - /// - /// The buffer input geometru - /// - /// - /// The specified distance for buffering. - /// - /// - /// The resulting buffers - /// - public async Task GeodesicBuffer(Geometry geometry, double distance) - { - return await InvokeAsync("geodesicBuffer", geometry, distance, null); - } - - /// - /// Creates geodesic buffer polygons at a specified distance around the input geometries. When calculating distances, this method takes the curvature of the earth into account, which provides highly accurate results when dealing with very large geometries and/or geometries that spatially vary on a global scale where one projected coordinate system could not accurately plot coordinates and measure distances for all the geometries. - /// - /// - /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. In general, if your input geometries are assigned one of those two spatial references, you should always use geodesicBuffer() to obtain the - /// most accurate results for those geometries. If needing to buffer points assigned a projected coordinate system - /// other than Web Mercator, use buffer() instead. If the input geometries have a geographic coordinate system other - /// than WGS84 (wkid: 4326), use geometryService.buffer(). - /// - /// - /// The buffer input geometru - /// - /// - /// The specified distance for buffering. - /// - /// - /// Measurement unit of the distance. Defaults to the units of the input geometry. - /// - /// - /// The resulting buffers - /// - public async Task GeodesicBuffer(Geometry geometry, double distance, GeometryEngineLinearUnit? unit) - { - return await InvokeAsync("geodesicBuffer", geometry, distance, unit); - } - - /// - /// Returns a geodesically densified version of the input geometry. Use this function to draw the line(s) of the geometry along great circles. - /// - /// - /// A polyline or polygon to densify. - /// - /// - /// The maximum segment length allowed (in meters if a maxSegmentLengthUnit is not provided). This must be a positive value. - /// - /// - /// Returns the densified geometry. - /// - public async Task GeodesicDensify(Geometry geometry, double maxSegmentLength) - { - return await InvokeAsync("geodesicDensify", geometry, maxSegmentLength, null); - } - - /// - /// Returns a geodesically densified version of the input geometry. Use this function to draw the line(s) of the geometry along great circles. - /// - /// - /// A polyline or polygon to densify. - /// - /// - /// The maximum segment length allowed (in meters if a maxSegmentLengthUnit is not provided). This must be a positive value. - /// - /// - /// Measurement unit for maxSegmentLength. If not provided, the unit will default to meters. - /// - /// - /// Returns the densified geometry. - /// - public async Task GeodesicDensify(Geometry geometry, double maxSegmentLength, - GeometryEngineLinearUnit? maxSegmentLengthUnit) - { - return await InvokeAsync("geodesicDensify", geometry, maxSegmentLength, maxSegmentLengthUnit); - } - - /// - /// Calculates the length of the input geometry. As opposed to planarLength(), geodesicLength() takes into account the curvature of the earth when performing this calculation. Therefore, when using input geometries with a spatial reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to calculate lengths using geodesicLength(). If the input geometries have a projected coordinate system other than Web Mercator, use planarLength() instead. - /// - /// - /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. - /// - /// - /// The input geometry. - /// - /// - /// Measurement unit of the return value. Defaults to the units of the input geometry. - /// - /// - /// Length of the input geometry. - /// - public async Task GeodesicLength(Geometry geometry, GeometryEngineLinearUnit? unit = null) - { - return await InvokeAsync("geodesicLength", geometry, unit); - } - - /// - /// Creates new geometries from the intersections between two geometries. If the input geometries have different dimensions (i.e. point = 0; polyline = 1; polygon = 2), then the result's dimension will be equal to the lowest dimension of the inputs. The table below describes the expected output for various combinations of geometry types. - /// - /// - /// The input array of geometries. - /// - /// - /// The geometry to intersect with geometries1. - /// - /// - /// The intersections of the geometries. - /// - public async Task Intersect(IEnumerable geometries1, Geometry geometry2) - { - return await InvokeAsync("intersect", geometries1, geometry2); - } - - /// - /// Creates new geometries from the intersections between two geometries. If the input geometries have different dimensions (i.e. point = 0; polyline = 1; polygon = 2), then the result's dimension will be equal to the lowest dimension of the inputs. - /// - /// - /// The input geometry. - /// - /// - /// The geometry to intersect with geometry1. - /// - /// - /// The intersections of the geometries. - /// - public async Task Intersect(Geometry geometry1, Geometry geometry2) - { - return await InvokeAsync("intersect", geometry1, geometry2); - } - - /// - /// Indicates if one geometry intersects another geometry. - /// - /// - /// The geometry that is tested for the intersects relationship to the other geometry. - /// - /// - /// The geometry being intersected. - /// - /// - /// Returns true if the input geometries intersect each other. - /// - public async Task Intersects(Geometry geometry1, Geometry geometry2) - { - return await InvokeAsync("intersects", geometry1, geometry2); - } - - /// - /// Indicates if the given geometry is topologically simple. In a simplified geometry, no polygon rings or polyline paths will overlap, and no self-intersection will occur. - /// - /// - /// The input geometry. - /// - /// - /// Returns true if the geometry is topologically simple. - /// - public async Task IsSimple(Geometry geometry) - { - return await InvokeAsync("isSimple", geometry); - } - - /// - /// Finds the coordinate of the geometry that is closest to the specified point. - /// - /// - /// The geometry to consider. - /// - /// - /// The point used to search the nearest coordinate in the geometry. - /// - /// - /// Returns an object containing the nearest coordinate. - /// - public async Task NearestCoordinate(Geometry geometry, Point inputPoint) - { - return await InvokeAsync("nearestCoordinate", geometry, inputPoint); - } - - /// - /// Finds the vertex on the geometry nearest to the specified point. - /// - /// - /// The geometry to consider. - /// - /// - /// The point used to search the nearest vertex in the geometry. - /// - /// - /// Returns an object containing the nearest vertex. - /// - public async Task NearestVertex(Geometry geometry, Point inputPoint) - { - return await InvokeAsync("nearestVertex", geometry, inputPoint); - } - - /// - /// Finds all vertices in the given distance from the specified point, sorted from the closest to the furthest and returns them as an array of Objects. - /// - /// - /// The geometry to consider. - /// - /// - /// The point from which to measure. - /// - /// - /// The distance to search from the inputPoint in the units of the view's spatial reference. - /// - /// - /// The maximum number of vertices to return. - /// - /// - /// An array of objects containing the nearest vertices within the given searchRadius. - /// - public async Task NearestVertices(Geometry geometry, Point inputPoint, double searchRadius, int maxVertexCountToReturn) - { - return await InvokeAsync("nearestVertices", geometry, inputPoint, searchRadius, maxVertexCountToReturn); - } - - /// - /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is similar to buffering, but produces a one-sided result. - /// - /// - /// The geometries to offset. - /// - /// - /// The planar distance to offset from the input geometry. If offsetDistance > 0, then the offset geometry is constructed to the right of the oriented input geometry, if offsetDistance = 0, then there is no change in the geometries, otherwise it is constructed to the left. For a simple polygon, the orientation of outer rings is clockwise and for inner rings it is counter clockwise. So the "right side" of a simple polygon is always its inside. - /// - /// - /// The offset geometries. - /// - public async Task Offset(IEnumerable geometries, double offsetDistance) - { - return await InvokeAsync("offset", geometries, offsetDistance, null, null, null, null); - } - - /// - /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is similar to buffering, but produces a one-sided result. - /// - /// - /// The geometries to offset. - /// - /// - /// The planar distance to offset from the input geometry. If offsetDistance > 0, then the offset geometry is constructed to the right of the oriented input geometry, if offsetDistance = 0, then there is no change in the geometries, otherwise it is constructed to the left. For a simple polygon, the orientation of outer rings is clockwise and for inner rings it is counter clockwise. So the "right side" of a simple polygon is always its inside. - /// - /// - /// Measurement unit of the offset distance. Defaults to the units of the input geometries. - /// - /// - /// The offset geometries. - /// - public async Task Offset(IEnumerable geometries, double offsetDistance, - GeometryEngineLinearUnit? offsetUnit) - { - return await InvokeAsync("offset", geometries, offsetDistance, - offsetUnit, null, null, null); - } - - /// - /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is similar to buffering, but produces a one-sided result. - /// - /// - /// The geometries to offset. - /// - /// - /// The planar distance to offset from the input geometry. If offsetDistance > 0, then the offset geometry is constructed to the right of the oriented input geometry, if offsetDistance = 0, then there is no change in the geometries, otherwise it is constructed to the left. For a simple polygon, the orientation of outer rings is clockwise and for inner rings it is counter clockwise. So the "right side" of a simple polygon is always its inside. - /// - /// - /// Measurement unit of the offset distance. Defaults to the units of the input geometries. - /// - /// - /// The - /// - /// - /// The offset geometries. - /// - public async Task Offset(IEnumerable geometries, double offsetDistance, - GeometryEngineLinearUnit? offsetUnit, JoinType? joinType) - { - return await InvokeAsync("offset", geometries, offsetDistance, - offsetUnit, joinType, null, null); - } - - /// - /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is similar to buffering, but produces a one-sided result. - /// - /// - /// The geometries to offset. - /// - /// - /// The planar distance to offset from the input geometry. If offsetDistance > 0, then the offset geometry is constructed to the right of the oriented input geometry, if offsetDistance = 0, then there is no change in the geometries, otherwise it is constructed to the left. For a simple polygon, the orientation of outer rings is clockwise and for inner rings it is counter clockwise. So the "right side" of a simple polygon is always its inside. - /// - /// - /// Measurement unit of the offset distance. Defaults to the units of the input geometries. - /// - /// - /// The - /// - /// - /// Applicable when joinType = 'miter'; bevelRatio is multiplied by the offset distance and the result determines how far a mitered offset intersection can be located before it is beveled. - /// - /// - /// The offset geometries. - /// - public async Task Offset(IEnumerable geometries, double offsetDistance, - GeometryEngineLinearUnit? offsetUnit, JoinType? joinType, double? bevelRatio) - { - return await InvokeAsync("offset", geometries, offsetDistance, - offsetUnit, joinType, bevelRatio, null); - } - - /// - /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is similar to buffering, but produces a one-sided result. - /// - /// - /// The geometries to offset. - /// - /// - /// The planar distance to offset from the input geometry. If offsetDistance > 0, then the offset geometry is constructed to the right of the oriented input geometry, if offsetDistance = 0, then there is no change in the geometries, otherwise it is constructed to the left. For a simple polygon, the orientation of outer rings is clockwise and for inner rings it is counter clockwise. So the "right side" of a simple polygon is always its inside. - /// - /// - /// Measurement unit of the offset distance. Defaults to the units of the input geometries. - /// - /// - /// The - /// - /// - /// Applicable when joinType = 'miter'; bevelRatio is multiplied by the offset distance and the result determines how far a mitered offset intersection can be located before it is beveled. - /// - /// - /// Applicable when joinType = 'round'; flattenError determines the maximum distance of the resulting segments compared to the true circular arc. The algorithm never produces more than around 180 vertices for each round join. - /// - /// - /// The offset geometries. - /// - public async Task Offset(IEnumerable geometries, double offsetDistance, - GeometryEngineLinearUnit? offsetUnit, JoinType? joinType, double? bevelRatio, - double? flattenError) - { - return await InvokeAsync("offset", geometries, offsetDistance, offsetUnit, joinType, bevelRatio, flattenError); - } - - /// - /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is similar to buffering, but produces a one-sided result. - /// - /// - /// The geometry to offset. - /// - /// - /// The planar distance to offset from the input geometry. If offsetDistance > 0, then the offset geometry is constructed to the right of the oriented input geometry, if offsetDistance = 0, then there is no change in the geometries, otherwise it is constructed to the left. For a simple polygon, the orientation of outer rings is clockwise and for inner rings it is counter clockwise. So the "right side" of a simple polygon is always its inside. - /// - /// - /// The offset geometry. - /// - public async Task Offset(Geometry geometry, double offsetDistance) - { - return await InvokeAsync("offset", geometry, offsetDistance, null, null, null, null); - } - - /// - /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is similar to buffering, but produces a one-sided result. - /// - /// - /// The geometry to offset. - /// - /// - /// The planar distance to offset from the input geometry. If offsetDistance > 0, then the offset geometry is constructed to the right of the oriented input geometry, if offsetDistance = 0, then there is no change in the geometries, otherwise it is constructed to the left. For a simple polygon, the orientation of outer rings is clockwise and for inner rings it is counter clockwise. So the "right side" of a simple polygon is always its inside. - /// - /// - /// Measurement unit of the offset distance. Defaults to the units of the input geometries. - /// - /// - /// The offset geometry. - /// - public async Task Offset(Geometry geometry, double offsetDistance, - GeometryEngineLinearUnit? offsetUnit) - { - return await InvokeAsync("offset", geometry, offsetDistance, - offsetUnit, null, null, null); - } - - /// - /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is similar to buffering, but produces a one-sided result. - /// - /// - /// The geometry to offset. - /// - /// - /// The planar distance to offset from the input geometry. If offsetDistance > 0, then the offset geometry is constructed to the right of the oriented input geometry, if offsetDistance = 0, then there is no change in the geometries, otherwise it is constructed to the left. For a simple polygon, the orientation of outer rings is clockwise and for inner rings it is counter clockwise. So the "right side" of a simple polygon is always its inside. - /// - /// - /// Measurement unit of the offset distance. Defaults to the units of the input geometries. - /// - /// - /// The - /// - /// - /// The offset geometry. - /// - public async Task Offset(Geometry geometry, double offsetDistance, - GeometryEngineLinearUnit? offsetUnit, JoinType? joinType) - { - return await InvokeAsync("offset", geometry, offsetDistance, - offsetUnit, joinType, null, null); - } - - /// - /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is similar to buffering, but produces a one-sided result. - /// - /// - /// The geometry to offset. - /// - /// - /// The planar distance to offset from the input geometry. If offsetDistance > 0, then the offset geometry is constructed to the right of the oriented input geometry, if offsetDistance = 0, then there is no change in the geometries, otherwise it is constructed to the left. For a simple polygon, the orientation of outer rings is clockwise and for inner rings it is counter clockwise. So the "right side" of a simple polygon is always its inside. - /// - /// - /// Measurement unit of the offset distance. Defaults to the units of the input geometries. - /// - /// - /// The - /// - /// - /// Applicable when joinType = 'miter'; bevelRatio is multiplied by the offset distance and the result determines how far a mitered offset intersection can be located before it is beveled. - /// - /// - /// The offset geometry. - /// - public async Task Offset(Geometry geometry, double offsetDistance, - GeometryEngineLinearUnit? offsetUnit, JoinType? joinType, double? bevelRatio) - { - return await InvokeAsync("offset", geometry, offsetDistance, - offsetUnit, joinType, bevelRatio, null); - } - - /// - /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is similar to buffering, but produces a one-sided result. - /// - /// - /// The geometry to offset. - /// - /// - /// The planar distance to offset from the input geometry. If offsetDistance > 0, then the offset geometry is constructed to the right of the oriented input geometry, if offsetDistance = 0, then there is no change in the geometries, otherwise it is constructed to the left. For a simple polygon, the orientation of outer rings is clockwise and for inner rings it is counter clockwise. So the "right side" of a simple polygon is always its inside. - /// - /// - /// Measurement unit of the offset distance. Defaults to the units of the input geometries. - /// - /// - /// The - /// - /// - /// Applicable when joinType = 'miter'; bevelRatio is multiplied by the offset distance and the result determines how far a mitered offset intersection can be located before it is beveled. - /// - /// - /// Applicable when joinType = 'round'; flattenError determines the maximum distance of the resulting segments compared to the true circular arc. The algorithm never produces more than around 180 vertices for each round join. - /// - /// - /// The offset geometry. - /// - public async Task Offset(Geometry geometry, double offsetDistance, - GeometryEngineLinearUnit? offsetUnit, JoinType? joinType, double? bevelRatio, - double? flattenError) - { - return await InvokeAsync("offset", geometry, offsetDistance, offsetUnit, joinType, bevelRatio, flattenError); - } - - /// - /// Indicates if one geometry overlaps another geometry. - /// - /// - /// The base geometry that is tested for the "overlaps" relationship with the other geometry. - /// - /// - /// The comparison geometry that is tested for the "overlaps" relationship with the other geometry. - /// - /// - /// Returns true if the two geometries overlap. - /// - public async Task Overlaps(Geometry geometry1, Geometry geometry2) - { - return await InvokeAsync("overlaps", geometry1, geometry2); - } - - /// - /// Calculates the area of the input geometry. As opposed to geodesicArea(), planarArea() performs this calculation using projected coordinates and does not take into account the earth's curvature. When using input geometries with a spatial reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to calculate areas using geodesicArea(). If the input geometries have a projected coordinate system other than Web Mercator, use planarArea() instead. - /// - /// - /// The input polygon. - /// - public async Task PlanarArea(Polygon geometry) - { - return await InvokeAsync("planarArea", geometry, null); - } - - /// - /// Calculates the area of the input geometry. As opposed to geodesicArea(), planarArea() performs this calculation using projected coordinates and does not take into account the earth's curvature. When using input geometries with a spatial reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to calculate areas using geodesicArea(). If the input geometries have a projected coordinate system other than Web Mercator, use planarArea() instead. - /// - /// - /// The input polygon. - /// - /// - /// Measurement unit of the return value. Defaults to the units of the input geometries. - /// - /// - /// The area of the input geometry. - /// - public async Task PlanarArea(Polygon geometry, GeometryEngineAreaUnit? unit) - { - return await InvokeAsync("planarArea", geometry, unit); - } - - /// - /// Calculates the length of the input geometry. As opposed to geodesicLength(), planarLength() uses projected coordinates and does not take into account the curvature of the earth when performing this calculation. When using input geometries with a spatial reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to calculate lengths using geodesicLength(). If the input geometries have a projected coordinate system other than Web Mercator, use planarLength() instead. - /// - /// - /// The input geometry. - /// - /// - /// The length of the input geometry. - /// - public async Task PlanarLength(Geometry geometry) - { - return await InvokeAsync("planarLength", geometry); - } - - /// - /// Calculates the length of the input geometry. As opposed to geodesicLength(), planarLength() uses projected coordinates and does not take into account the curvature of the earth when performing this calculation. When using input geometries with a spatial reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to calculate lengths using geodesicLength(). If the input geometries have a projected coordinate system other than Web Mercator, use planarLength() instead. - /// - /// - /// The input geometry. - /// - /// - /// Measurement unit of the return value. Defaults to the units of the input geometries. - /// - /// - /// The length of the input geometry. - /// - public async Task PlanarLength(Geometry geometry, GeometryEngineLinearUnit? unit) - { - return await InvokeAsync("planarLength", geometry, unit); - } - - /// - /// Indicates if the given DE-9IM relation is true for the two geometries. - /// - /// - /// The first geometry for the relation. - /// - /// - /// The second geometry for the relation. - /// - /// - /// The Dimensionally Extended 9 Intersection Model (DE-9IM) matrix relation (encoded as a string) to test against the relationship of the two geometries. This string contains the test result of each intersection represented in the DE-9IM matrix. Each result is one character of the string and may be represented as either a number (maximum dimension returned: 0,1,2), a Boolean value (T or F), or a mask character (for ignoring results: '*'). For example, each of the following DE-9IM string codes are valid for testing whether a polygon geometry completely contains a line geometry: TTTFFTFFT (Boolean), 'T******FF*' (ignore irrelevant intersections), or '102FF*FF*' (dimension form). Each returns the same result. See - /// this article and - /// - /// this - /// ArcGIS help page - /// - /// for more information about the DE-9IM model and how string codes are constructed. - /// - /// - /// Returns true if the relation of the input geometries is accurate. - /// - public async Task Relate(Geometry geometry1, Geometry geometry2, string relation) - { - return await InvokeAsync("relate", geometry1, geometry2, relation); - } - - /// - /// Rotates a geometry counterclockwise by the specified number of degrees. Rotation is around the centroid, or a given rotation point. - /// - /// - /// The geometry to rotate. - /// - /// - /// The rotation angle in degrees. - /// - /// - /// Point to rotate the geometry around. Defaults to the centroid of the geometry. - /// - /// - /// The rotated geometry. - /// - public async Task Rotate(Geometry geometry, double angle, Point rotationOrigin) - { - return await InvokeAsync("rotate", geometry, angle, rotationOrigin); - } - - /// - /// Performs the simplify operation on the geometry, which alters the given geometries to make their definitions topologically legal with respect to their geometry type. At the end of a simplify operation, no polygon rings or polyline paths will overlap, and no self-intersection will occur. - /// - /// - /// The geometry to be simplified. - /// - /// - /// The simplified geometry. - /// - public async Task Simplify(Geometry geometry) - { - return await InvokeAsync("simplify", geometry); - } - - /// - /// Creates the symmetric difference of two geometries. The symmetric difference includes the parts that are in either of the sets, but not in both. - /// - /// - /// One of the Geometry instances in the XOR operation. - /// - /// - /// One of the Geometry instances in the XOR operation. - /// - /// - /// The symmetric differences of the two geometries. - /// - public async Task SymmetricDifference(IEnumerable leftGeometries, Geometry rightGeometry) - { - return await InvokeAsync("symmetricDifference", leftGeometries, rightGeometry); - } - - /// - /// Creates the symmetric difference of two geometries. The symmetric difference includes the parts that are in either of the sets, but not in both. - /// - /// - /// One of the Geometry instances in the XOR operation. - /// - /// - /// One of the Geometry instances in the XOR operation. - /// - /// - /// The symmetric differences of the two geometries. - /// - public async Task SymmetricDifference(Geometry leftGeometry, Geometry rightGeometry) - { - return await InvokeAsync("symmetricDifference", leftGeometry, rightGeometry); - } - - /// - /// Indicates if one geometry touches another geometry. - /// - /// - /// The geometry to test the "touches" relationship with the other geometry. - /// - /// - /// The geometry to be touched. - /// - /// - /// When true, geometry1 touches geometry2. - /// - public async Task Touches(Geometry geometry1, Geometry geometry2) - { - return await InvokeAsync("touches", geometry1, geometry2); - } - - /// - /// All inputs must be of the same type of geometries and share one spatial reference. - /// - /// - /// An array of Geometries to union. - /// - /// - /// The union of the geometries - /// - public async Task Union(params Geometry[] geometries) - { - return await InvokeAsync("union", geometries.Cast()); - } - - /// - /// All inputs must be of the same type of geometries and share one spatial reference. - /// - /// - /// An array of Geometries to union. - /// - /// - /// The union of the geometries - /// - public async Task Union(IEnumerable geometries) - { - return await InvokeAsync("union", geometries.Cast()); - } - - /// - /// Indicates if one geometry is within another geometry. - /// - /// - /// The base geometry that is tested for the "within" relationship to the other geometry. - /// - /// - /// The comparison geometry that is tested for the "contains" relationship to the other geometry. - /// - /// - /// Returns true if innerGeometry is within outerGeometry. - /// - public async Task Within(Geometry innerGeometry, Geometry outerGeometry) - { - return await InvokeAsync("within", innerGeometry, outerGeometry); - } - - /// - /// Creates a new instance of this class and initializes it with values from a JSON object generated from an ArcGIS product. The object passed into the input json parameter often comes from a response to a query operation in the REST API or a toJSON() method from another ArcGIS product. See the Using fromJSON() topic in the Guide for details and examples of when and how to use this function. - /// - /// - /// A JSON representation of the instance in the ArcGIS format. See the ArcGIS REST API documentation for examples of the structure of various input JSON objects. - /// - /// - /// Returns a new geometry instance. - /// - public async Task FromArcGisJson(string json) - where T : Geometry - { - return await InvokeAsync("fromJSON", json, typeof(T).Name); - } - - /// - /// Converts an instance of this class to its ArcGIS portal JSON representation. See the Using fromJSON() guide topic for more information. - /// - /// - /// The geometry to convert. - /// - /// - /// The ArcGIS portal JSON representation of an instance of this class. - /// - public async Task ToArcGisJson(T geometry) - where T : Geometry - { - return await InvokeAsync("toJSON", geometry); - } - - /// - /// Creates a deep clone of the geometry. - /// - /// - /// Unlike the Clone methods in the Geometry classes, this method does a loop through the ArcGIS JS SDK. Therefore, if you are having issues with unpopulated fields in the geometry, try using this method instead. - /// - public async Task Clone(T geometry) - where T : Geometry - { - return await InvokeAsync("clone", geometry); - } - - /// - /// Centers the given extent to the given point, and returns a new extent. - /// - /// - /// The input extent. - /// - /// - /// The point to center the extent on. - /// - /// - /// The centered extent. - /// - public async Task CenterExtentAt(Extent extent, Point point) - { - return await InvokeAsync("centerExtentAt", extent, point); - } - - /// - /// Expands the extent by the given factor. For example, a value of 1.5 will expand the extent to be 50 percent larger than the original extent. - /// - /// - /// The input extent. - /// - /// - /// The factor by which to expand the extent. - /// - /// - /// The expanded extent. - /// - public async Task Expand(Extent extent, double factor) - { - return await InvokeAsync("expand", extent, factor); - } - - /// - /// Returns an array with either one Extent that's been shifted to within +/- 180 or two Extents if the original extent intersects the International Dateline. - /// - /// - /// The input extent. - /// - /// - /// An array with either one Extent that's been shifted to within +/- 180 or two Extents if the original extent intersects the International Dateline. - /// - public async Task NormalizeExtent(Extent extent) - { - return await InvokeAsync("normalizeExtent", extent); - } - - /// - /// Modifies the extent geometry in-place with X and Y offsets in map units. - /// - /// - /// The input extent. - /// - /// - /// The offset distance in map units for the X-coordinate. - /// - /// - /// The offset distance in map units for the Y-coordinate. - /// - /// - /// The offset distance in map units for the Z-coordinate. - /// - /// - public async Task OffsetExtent(Extent extent, double dx, double dy, double dz = 0) - { - return await InvokeAsync("offsetExtent", extent, dx, dy, dz); - } - - /// - /// Modifies the point geometry in-place by shifting the X-coordinate to within +/- 180 span in map units. - /// - /// - /// The input point. - /// - /// - /// Returns a point with a normalized x-value. - /// - public async Task NormalizePoint(Point point) - { - return await InvokeAsync("normalizePoint", point); - } - - /// - /// Adds a path, or line segment, to the polyline. When added, the index of the path is incremented by one. - /// - /// - /// The polyline to add the path to. Will return a new modified copy. - /// - /// - /// The polyline path to add as a . - /// - /// - /// Returns a new polyline with the added path. - /// - public async Task AddPath(Polyline polyline, MapPath points) - { - return await InvokeAsync("addPath", polyline, points); - } - - /// - /// Adds a path, or line segment, to the polyline. When added, the index of the path is incremented by one. - /// - /// - /// The polyline to add the path to. Will return a new modified copy. - /// - /// - /// The polyline path to add as an array of s. - /// - /// - /// Returns a new polyline with the added path. - /// - public async Task AddPath(Polyline polyline, Point[] points) - { - var mapPoints = new List(); - foreach (Point p in points) - { - mapPoints.Add(new MapPoint(p.X!.Value, p.Y!.Value)); - } - - return await AddPath(polyline, new MapPath(mapPoints)); - } - - /// - /// Returns a point specified by a path and point in the path. - /// - /// - /// The polyline to get the point from. - /// - /// - /// The index of the path in the polyline. - /// - /// - /// The index of the point in the path. - /// - /// - /// Returns the point along the Polyline located in the given path and point indices. - /// - public async Task GetPoint(Polyline polyline, int pathIndex, int pointIndex) - { - return await InvokeAsync("getPointOnPolyline", polyline, pathIndex, pointIndex); - } - - /// - /// Inserts a new point into a polyline and returns the modified line. - /// - /// - /// The polyline to insert the point into. - /// - /// - /// The index of the path in which to insert a point. - /// - /// - /// The index of the inserted point in the path. - /// - /// - /// The point to insert into the polyline. - /// - /// - /// Returns a new polyline with the inserted point. - /// - public async Task InsertPoint(Polyline polyline, int pathIndex, int pointIndex, Point point) - { - return await InvokeAsync("insertPointOnPolyline", polyline, pathIndex, pointIndex, point); - } - - /// - /// Removes a path from the Polyline. The index specifies which path to remove. - /// - /// - /// The polyline to remove the path from. - /// - /// - /// The index of the path to remove. - /// - /// - /// Returns an object with the modified polyline and the removed path. - /// - public async Task<(Polyline PolyLine, Point[] Path)> RemovePath(Polyline polyline, int index) - { - return await InvokeAsync<(Polyline PolyLine, Point[] Path)>("removePath", polyline, index); - } - - /// - /// Removes a point from the polyline at the given pointIndex within the path identified by the given pathIndex. - /// - /// - /// The polyline to remove the point from. - /// - /// - /// The index of the path in which to remove a point. - /// - /// - /// The index of the point in the path to remove. - /// - /// - /// Returns an object with the modified polyline and the removed point. - /// - public async Task<(Polyline PolyLine, Point Point)> RemovePoint(Polyline polyline, int pathIndex, int pointIndex) - { - return await InvokeAsync<(Polyline PolyLine, Point Point)>("removePointOnPolyline", polyline, pathIndex, pointIndex); - } - - /// - /// Updates a point in a polyline and returns the modified polyline. - /// - /// - /// The polyline to update the point in. - /// - /// - /// The index of the path in which to update a point. - /// - /// - /// The index of the point in the path to update. - /// - /// - /// The new point to update the polyline with. - /// - /// - /// Returns a new polyline with the updated point. - /// - public async Task SetPoint(Polyline polyline, int pathIndex, int pointIndex, Point point) - { - return await InvokeAsync("setPointOnPolyline", polyline, pathIndex, pointIndex, point); - } - - /// - /// Adds a ring to the Polygon. The ring can be one of the following: an array of numbers or an array of points. When added the index of the ring is incremented by one. - /// - /// - /// The polygon to add the ring to. - /// - /// - /// A polygon ring. The first and last coordinates/points in the ring must be the same. - /// - /// - /// Returns a new polygon with the added ring. - /// - public async Task AddRing(Polygon polygon, MapPath points) - { - return await InvokeAsync("addRing", polygon, points); - } - - /// - /// Adds a ring to the Polygon. The ring can be one of the following: an array of numbers or an array of points. When added the index of the ring is incremented by one. - /// - /// - /// The polygon to add the ring to. - /// - /// - /// A polygon ring. The first and last coordinates/points in the ring must be the same. - /// - /// - /// Returns a new polygon with the added ring. - /// - public async Task AddRing(Polygon polygon, Point[] points) - { - var mapPoints = new List(); - foreach (Point p in points) - { - mapPoints.Add(new MapPoint(p.X!.Value, p.Y!.Value)); - } - - return await AddRing(polygon, new MapPath(mapPoints)); - } - - /// - /// Converts the given Extent to a Polygon instance. This is useful for scenarios in which you would like to display an area of interest, which is typically defined by an Extent or bounding box, as a polygon with a fill symbol in the view. Some geoprocessing tools require input geometries to be of a Polygon type and not an Extent. - /// - /// - /// An extent object to convert to a polygon. - /// - /// - /// A polygon instance representing the given extent. - /// - public async Task PolygonFromExtent(Extent extent) - { - return await InvokeAsync("fromExtent", extent); - } - - /// - /// Returns a point specified by a ring and point in the ring. - /// - /// - /// The polygon to get the point from. - /// - /// - /// The index of the ring containing the desired point. - /// - /// - /// The index of the desired point within the ring. - /// - /// - /// Returns the point at the specified ring index and point index. - /// - public async Task GetPoint(Polygon polygon, int ringIndex, int pointIndex) - { - return await InvokeAsync("getPointOnPolygon", polygon, ringIndex, pointIndex); - } - - /// - /// Inserts a new point into the polygon. - /// - /// - /// The polygon to insert the point into. - /// - /// - /// The index of the ring in which to insert the point. - /// - /// - /// The index of the point to insert within the ring. - /// - /// - /// The point to insert into the polygon. - /// - /// - /// Returns a new polygon with the inserted point. - /// - public async Task InsertPoint(Polygon polygon, int ringIndex, int pointIndex, Point point) - { - return await InvokeAsync("insertPointOnPolygon", polygon, ringIndex, pointIndex, point); - } - - /// - /// Checks if a Polygon ring is clockwise - /// - /// - /// The polygon to check the ring on. - /// - /// - /// A polygon ring defined as a . The first and last coordinates/points in the ring must be the same. - /// - /// - /// Returns true if the ring is clockwise and false for counterclockwise. - /// - public async Task IsClockwise(Polygon polygon, MapPath ring) - { - return await InvokeAsync("isClockwise", polygon, ring); - } - - /// - /// Checks if a Polygon ring is clockwise - /// - /// - /// The polygon to check the ring on. - /// - /// - /// A polygon ring defined as an array of s. The first and last coordinates/points in the ring must be the same. - /// - /// - /// Returns true if the ring is clockwise and false for counterclockwise. - /// - public async Task IsClockwise(Polygon polygon, Point[] ring) - { - var mapPoints = new List(); - foreach (Point p in ring) - { - mapPoints.Add(new MapPoint(p.X!.Value, p.Y!.Value)); - } - - return await IsClockwise(polygon, new MapPath(mapPoints)); - } - - /// - /// Removes a point from the polygon at the given pointIndex within the ring identified by the given ringIndex. - /// - /// - /// The polyline to remove the point from. - /// - /// - /// The index of the ring containing the point to remove. - /// - /// - /// The index of the point to remove within the ring. - /// - /// - /// Returns an object with the modified polygon and the removed point. - /// - public async Task<(Polygon Polygon, Point Point)> RemovePoint(Polygon polygon, int ringIndex, int pointIndex) - { - return await InvokeAsync<(Polygon Polygon, Point Point)>("removePointOnPolygon", polygon, ringIndex, pointIndex); - } - - /// - /// Removes a ring from the Polygon. The index specifies which ring to remove. - /// - /// - /// The polygon to remove the ring from. - /// - /// - /// The index of the ring to remove. - /// - /// - /// Returns an object with the modified polygon and the removed ring. - /// - public async Task<(Polygon Polygon, Point[] Ring)> RemoveRing(Polygon polygon, int index) - { - return await InvokeAsync<(Polygon Polygon, Point[] Ring)>("removeRing", polygon, index); - } - - /// - /// Updates a point in a polygon and returns the modified polygon. - /// - /// - /// The polygon to update the point in. - /// - /// - /// The index of the ring containing the point to update. - /// - /// - /// The index of the point to update within the ring. - /// - /// - /// The new point to update the polygon with. - /// - /// - /// Returns a new polygon with the updated point. - /// - public async Task SetPoint(Polygon polygon, int ringIndex, int pointIndex, Point point) - { - return await InvokeAsync("setPointOnPolygon", polygon, ringIndex, pointIndex, point); - } - - /// - /// Retrieves the center point of the extent in map units. - /// - public async Task GetExtentCenter(Extent extent) - { - return await InvokeAsync("getExtentCenter", extent); - } - - /// - /// Retrieves the height of the extent in map units (the distance between ymin and ymax). - /// - public async Task GetExtentHeight(Extent extent) - { - return await InvokeAsync("getExtentHeight", extent); - } - - /// - /// Retrieves the width of the extent in map units (the distance between xmin and xmax). - /// - public async Task GetExtentWidth(Extent extent) - { - return await InvokeAsync("getExtentWidth", extent); - } -} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Components/Layers/Layer.cs b/src/dymaptic.GeoBlazor.Core/Components/Layers/Layer.cs index c60becd34..eafe7685d 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/Layers/Layer.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/Layers/Layer.cs @@ -7,8 +7,7 @@ public abstract partial class Layer : MapComponent /// Used internally to identify the sub type of Layer /// public abstract LayerType Type { get; } - - + /// /// For layers that are public and throw an error when given an ApiKey or Token, /// this setting allows you to exclude the ApiKey or Token from this layer's request. @@ -17,7 +16,7 @@ public abstract partial class Layer : MapComponent [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [CodeGenerationIgnore] public bool? ExcludeApiKey { get; set; } - + /// /// Indicates whether the layer's resources have loaded. /// default false @@ -28,7 +27,7 @@ public abstract partial class Layer : MapComponent [JsonInclude] [CodeGenerationIgnore] public bool? Loaded { get; internal set; } - + /// /// The opacity of the layer. /// default 1 @@ -52,7 +51,6 @@ public abstract partial class Layer : MapComponent [JsonIgnore] public LayerView? LayerView { get; internal set; } - /// /// Indicates how the layer should display in the [LayerList](https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-LayerList.html) widget. /// default "show" @@ -61,7 +59,7 @@ public abstract partial class Layer : MapComponent [Parameter] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ListMode? ListMode { get; set; } - + /// /// If the layer is added to the , this flag identifies the layer as a reference layer, which will sit on top of other layers to add labels. /// ArcGIS Maps SDK for JavaScript @@ -78,7 +76,7 @@ public abstract partial class Layer : MapComponent [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonConverter(typeof(FullExtentConverter))] public Extent? FullExtent { get; set; } - + /// /// Enable persistence of the layer in a [WebMap](https://developers.arcgis.com/javascript/latest/api-reference/esri-WebMap.html) or [WebScene](https://developers.arcgis.com/javascript/latest/api-reference/esri-WebScene.html). /// default true @@ -102,80 +100,55 @@ public abstract partial class Layer : MapComponent /// public bool Imported { get; set; } -#region PropertySetters - - /// - /// Asynchronously set the value of the FullExtent property after render. - /// - public virtual async Task SetFullExtent(Extent? value) + /// + public override async ValueTask DisposeAsync() { - FullExtent = value; - ModifiedParameters["FullExtent"] = value; - - if (JsComponentReference is null) + try { - return; - } - - await JsComponentReference!.InvokeVoidAsync("setFullExtent", - CancellationTokenSource.Token, - value); - } - - /// - /// Asynchronously set the value of the Opacity property after render. - /// - public async Task SetOpacity(double value) - { - Opacity = value; - ModifiedParameters["Opacity"] = value; + if (AbortManager is not null) + { + await AbortManager.DisposeAsync(); + } - if (JsComponentReference is null) + if (LayerView is not null) + { + await LayerView.DisposeAsync(); + } + + if (JsComponentReference is not null) + { + try + { + await JsComponentReference.DisposeAsync(); + } + catch (JSDisconnectedException) + { + // ignore, we have disconnected from the JS runtime + } + } + } + catch (TaskCanceledException) { - return; + // user cancelled } - - await JsComponentReference!.InvokeVoidAsync("setProperty", - CancellationTokenSource.Token, - "opacity", - value); - } - -#endregion - -#region Property Getters - - /// - /// Asynchronously retrieve the current value of the FullExtent property. - /// - public async Task GetFullExtent() - { - if (JsComponentReference is null) + catch (JSDisconnectedException) { - return null; + // lost connection (page navigation) } - - return await JsComponentReference!.InvokeAsync("getFullExtent", - CancellationTokenSource.Token); - } - - /// - /// Asynchronously retrieve the current value of the Opacity property. - /// - public async Task GetOpacity() - { - if (JsComponentReference is null) + catch (JSException) { - return null; + // instance already destroyed } - - return await JsComponentReference!.InvokeAsync("getProperty", - CancellationTokenSource.Token, - "opacity"); - } + await base.DisposeAsync(); + } -#endregion + /// + public override void ValidateRequiredChildren() + { + FullExtent?.ValidateRequiredChildren(); + base.ValidateRequiredChildren(); + } /// public override async Task RegisterChildComponent(MapComponent child) @@ -186,6 +159,7 @@ public override async Task RegisterChildComponent(MapComponent child) if (!extent.Equals(FullExtent)) { FullExtent = extent; + if (MapRendered) { await UpdateLayer(); @@ -195,10 +169,12 @@ public override async Task RegisterChildComponent(MapComponent child) break; default: await base.RegisterChildComponent(child); + if (MapRendered) { await UpdateLayer(); } + break; } } @@ -210,7 +186,7 @@ public override async Task UnregisterChildComponent(MapComponent child) { case Extent _: FullExtent = null; - + break; default: @@ -220,49 +196,6 @@ public override async Task UnregisterChildComponent(MapComponent child) } } - /// - public override async ValueTask DisposeAsync() - { - try - { - if (AbortManager is not null) - { - await AbortManager.DisposeAsync(); - } - - if (LayerView is not null) - { - await LayerView.DisposeAsync(); - } - - if (JsComponentReference is not null) - { - try - { - await JsComponentReference.DisposeAsync(); - } - catch (JSDisconnectedException) - { - // ignore, we have disconnected from the JS runtime - } - } - } - catch (TaskCanceledException) - { - // user cancelled - } - catch (JSDisconnectedException) - { - // lost connection (page navigation) - } - catch (JSException) - { - // instance already destroyed - } - - await base.DisposeAsync(); - } - /// /// Loads the resources referenced by this class. This method automatically executes for a View and all of the resources it references in Map if the view is constructed with a map instance. /// This method must be called by the developer when accessing a resource that will not be loaded in a View. @@ -323,7 +256,7 @@ public virtual async Task Load(CancellationToken cancellationToken) "Layers not defined in a Map or in Blazor Markup must be loaded with a JsModuleManager and IJSRuntime. Use the Load overload which takes these parameters."); } } - + AbortManager = new AbortManager(CoreJsModule!); IJSObjectReference abortSignal = await AbortManager!.CreateAbortSignal(cancellationToken); @@ -331,20 +264,25 @@ public virtual async Task Load(CancellationToken cancellationToken) await CoreJsModule!.InvokeVoidAsync("loadProtobuf", cancellationToken); await CoreJsModule.InvokeAsync("buildJsLayer", + // ReSharper disable once RedundantCast cancellationToken, (object)this, Id, View?.Id); - - JsComponentReference ??= await CoreJsModule.InvokeAsync( - "getJsComponent", cancellationToken, Id); - - IJSStreamReference streamRef = await JsComponentReference!.InvokeAsync("load", + + JsComponentReference ??= + await CoreJsModule.InvokeAsync("getJsComponent", cancellationToken, Id); + + IJSStreamReference streamRef = await JsComponentReference!.InvokeAsync("load", cancellationToken, abortSignal); Type type = GetType(); - Layer? deserializedLayer = await streamRef.ReadJsStreamReferenceAsJSON(type) as Layer; + + Layer? deserializedLayer = await streamRef.ReadJsStreamReferenceAsJSON(type, + cancellationToken: cancellationToken) as Layer; + if (deserializedLayer is null) { throw new InvalidOperationException($"Could not load layer of type {type.Name}"); } + CopyProperties(deserializedLayer); await UpdateFromJavaScript(deserializedLayer); await AbortManager.DisposeAbortController(cancellationToken); @@ -357,14 +295,8 @@ public override async ValueTask Refresh() { await UpdateLayer(); } - await base.Refresh(); - } - /// - public override void ValidateRequiredChildren() - { - FullExtent?.ValidateRequiredChildren(); - base.ValidateRequiredChildren(); + await base.Refresh(); } /// @@ -382,19 +314,6 @@ public override void ValidateRequiredChildren() return renderedLayer; } - /// - /// Copies values from the rendered JavaScript layer back to the .NET implementation. - /// - /// - /// The layer deserialized from JavaScript - /// - internal virtual Task UpdateFromJavaScript(Layer renderedLayer) - { - // This is called after MapComponent.CopyProperties, so it should only be used for properties - // that would fail to be copied by regular reflection-based copying. - return Task.CompletedTask; - } - /// public override async Task SetParametersAsync(ParameterView parameters) { @@ -402,6 +321,7 @@ public override async Task SetParametersAsync(ParameterView parameters) await base.SetParametersAsync(parameters); bool layerChanged = false; + if (PreviousParameters is not null && MapRendered) { foreach (KeyValuePair kvp in dictionary) @@ -411,7 +331,7 @@ public override async Task SetParametersAsync(ParameterView parameters) { continue; } - + if (!PreviousParameters.TryGetValue(kvp.Key, out object? previousValue)) { if (MapRendered) @@ -428,16 +348,17 @@ public override async Task SetParametersAsync(ParameterView parameters) { Array prevArray = (Array)previousValue; Array currArray = (Array)kvp.Value!; - + if (prevArray.Length != currArray.Length) { if (MapRendered) { await UpdateLayer(); } + break; } - + for (int i = 0; i < prevArray.Length; i++) { if (!Equals(prevArray.GetValue(i), currArray.GetValue(i))) @@ -447,30 +368,33 @@ public override async Task SetParametersAsync(ParameterView parameters) await UpdateLayer(); layerChanged = true; } + break; } } - + if (layerChanged) break; } else if (paramType.IsGenericType) { ICollection prevCollection = (ICollection)previousValue; ICollection currCollection = (ICollection)kvp.Value!; - + if (prevCollection.Count != currCollection.Count) { if (MapRendered) { await UpdateLayer(); } + break; } - + IEnumerator prevEnumerator = prevCollection.GetEnumerator(); using var prevEnumerator1 = prevEnumerator as IDisposable; IEnumerator currEnumerator = currCollection.GetEnumerator(); using var currEnumerator1 = currEnumerator as IDisposable; + while (prevEnumerator.MoveNext() && currEnumerator.MoveNext()) { if (!Equals(prevEnumerator.Current, currEnumerator.Current)) @@ -479,6 +403,7 @@ public override async Task SetParametersAsync(ParameterView parameters) { await UpdateLayer(); } + break; } } @@ -489,22 +414,13 @@ public override async Task SetParametersAsync(ParameterView parameters) { await UpdateLayer(); } + break; } } } - - PreviousParameters = dictionary.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); - } - /// - protected override async Task OnAfterRenderAsync(bool firstRender) - { - await base.OnAfterRenderAsync(firstRender); - if (_delayedUpdate) - { - await UpdateLayer(); - } + PreviousParameters = dictionary.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); } /// @@ -517,7 +433,7 @@ public async Task UpdateLayer() // don't update until the component has been returned from JavaScript return; } - + if (MapRendered && !_delayedUpdate) { // for components added after the map has rendered, wait one render cycle to get all children before updating @@ -526,12 +442,113 @@ public async Task UpdateLayer() return; } - + _delayedUpdate = false; // ReSharper disable once RedundantCast - await JsComponentReference!.InvokeAsync("updateComponent", CancellationTokenSource.Token, (object)this); + await JsComponentReference!.InvokeAsync("updateComponent", CancellationTokenSource.Token, + (object)this); + } + + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + + if (_delayedUpdate) + { + await UpdateLayer(); + } + } + + /// + /// Copies values from the rendered JavaScript layer back to the .NET implementation. + /// + /// + /// The layer deserialized from JavaScript + /// + internal virtual Task UpdateFromJavaScript(Layer renderedLayer) + { + // This is called after MapComponent.CopyProperties, so it should only be used for properties + // that would fail to be copied by regular reflection-based copying. + return Task.CompletedTask; } - + private bool _delayedUpdate; + + +#region PropertySetters + + /// + /// Asynchronously set the value of the FullExtent property after render. + /// + public virtual async Task SetFullExtent(Extent? value) + { + FullExtent = value; + ModifiedParameters["FullExtent"] = value; + + if (JsComponentReference is null) + { + return; + } + + await JsComponentReference!.InvokeVoidAsync("setFullExtent", + CancellationTokenSource.Token, + value); + } + + /// + /// Asynchronously set the value of the Opacity property after render. + /// + public async Task SetOpacity(double value) + { + Opacity = value; + ModifiedParameters["Opacity"] = value; + + if (JsComponentReference is null) + { + return; + } + + await JsComponentReference!.InvokeVoidAsync("setProperty", + CancellationTokenSource.Token, + "opacity", + value); + } + +#endregion + + +#region Property Getters + + /// + /// Asynchronously retrieve the current value of the FullExtent property. + /// + public async Task GetFullExtent() + { + if (JsComponentReference is null) + { + return null; + } + + return await JsComponentReference!.InvokeAsync("getFullExtent", + CancellationTokenSource.Token); + } + + /// + /// Asynchronously retrieve the current value of the Opacity property. + /// + public async Task GetOpacity() + { + if (JsComponentReference is null) + { + return null; + } + + return await JsComponentReference!.InvokeAsync("getProperty", + CancellationTokenSource.Token, + "opacity"); + } + +#endregion } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Components/LocationService.cs b/src/dymaptic.GeoBlazor.Core/Components/LocationService.cs deleted file mode 100644 index eebef77ec..000000000 --- a/src/dymaptic.GeoBlazor.Core/Components/LocationService.cs +++ /dev/null @@ -1,2208 +0,0 @@ -// ReSharper disable MethodOverloadWithOptionalParameter -namespace dymaptic.GeoBlazor.Core.Components; - -public partial class LocationService : LogicComponent -{ - /// - /// Default Constructor - /// - /// - /// Injected Identity Manager reference - /// - public LocationService(AuthenticationManager authenticationManager) : base(authenticationManager) - { - } - - /// - protected override string ComponentName => nameof(LocationService); - - // Final implementation of all the permutations of AddressesToLocations - private async Task> AddressesToLocationsImplementation(string url, object addresses, - string? countryCode, List? categories, LocationType? locationType, - SpatialReference? outSpatialReference, RequestOptions? requestOptions, - string? addressSearchStringParameterName) - { - IJSStreamReference streamRef = await InvokeAsync("addressesToLocations", url, - addresses, countryCode, categories, locationType, - outSpatialReference, requestOptions, addressSearchStringParameterName); - - return await streamRef.ReadJsStreamReferenceAsJSON>() ?? []; - } - - // final implementation of all the AddressToLocations permutations - private async Task> AddressToLocationsImplementation(string url, object address, - List? categories = null, string? countryCode = null, bool? forStorage = null, Point? location = null, - LocationType? locationType = null, string? magicKey = null, int? maxLocations = null, - List? outFields = null, SpatialReference? outSpatialReference = null, Extent? searchExtent = null, - RequestOptions? requestOptions = null, string? addressSearchStringParameterName = null) - { - IJSStreamReference streamRef = await InvokeAsync("addressToLocations", url, address, - categories, countryCode, forStorage, location, locationType, magicKey, - maxLocations, outFields, outSpatialReference, searchExtent, requestOptions, - addressSearchStringParameterName); - - return await streamRef.ReadJsStreamReferenceAsJSON>() ?? []; - } - - private const string ESRIGeoLocationUrl = "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer"; - - -#region AddressesToLocationsWithAddress - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - [CodeGenerationIgnore] - public async Task> AddressesToLocations(List
addresses) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses); - } - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - public async Task> AddressesToLocations(List
addresses, string? countryCode) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode); - } - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - public async Task> AddressesToLocations(List
addresses, string? countryCode, - List? categories) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode, categories); - } - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - public async Task> AddressesToLocations(List
addresses, string? countryCode, - List? categories, LocationType? locationType) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode, categories, locationType); - } - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - public async Task> AddressesToLocations(List
addresses, string? countryCode, - List? categories, LocationType? locationType, - SpatialReference? outSpatialReference) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode, categories, locationType, - outSpatialReference); - } - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Additional options to be used for the data request - /// - public async Task> AddressesToLocations(List
addresses, string? countryCode, - List? categories, LocationType? locationType, - SpatialReference? outSpatialReference, RequestOptions? requestOptions) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode, categories, locationType, - outSpatialReference, requestOptions); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - public Task> AddressesToLocations(string url, List
addresses) - { - return AddressesToLocations(url, addresses, null, null, null, null, null); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - public Task> AddressesToLocations(string url, List
addresses, - string? countryCode) - { - return AddressesToLocations(url, addresses, countryCode, null, null, null, null); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - public Task> AddressesToLocations(string url, List
addresses, - string? countryCode, List? categories) - { - return AddressesToLocations(url, addresses, countryCode, categories, null, null, null); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - public Task> AddressesToLocations(string url, List
addresses, - string? countryCode, List? categories, LocationType? locationType) - { - return AddressesToLocations(url, addresses, countryCode, categories, locationType, null, null); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - public Task> AddressesToLocations(string url, List
addresses, - string? countryCode, List? categories, LocationType? locationType, - SpatialReference? outSpatialReference) - { - return AddressesToLocations(url, addresses, countryCode, categories, locationType, outSpatialReference, null); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Additional options to be used for the data request - /// - public Task> AddressesToLocations(string url, List
addresses, - string? countryCode, List? categories, LocationType? locationType, - SpatialReference? outSpatialReference, RequestOptions? requestOptions) - { - return AddressesToLocationsImplementation(url, addresses, countryCode, categories, locationType, - outSpatialReference, requestOptions, null); - } - -#endregion - - -#region AddressesToLocationsWithString - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - [CodeGenerationIgnore] - public async Task> AddressesToLocations(List addresses) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses); - } - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - public async Task> AddressesToLocations(List addresses, string? countryCode) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode); - } - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - public async Task> AddressesToLocations(List addresses, string? countryCode, - List? categories) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode, categories); - } - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - public async Task> AddressesToLocations(List addresses, string? countryCode, - List? categories, LocationType? locationType) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode, categories, locationType); - } - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - public async Task> AddressesToLocations(List addresses, string? countryCode, - List? categories, LocationType? locationType, - SpatialReference? outSpatialReference) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode, categories, locationType, - outSpatialReference); - } - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Additional options to be used for the data request - /// - public async Task> AddressesToLocations(List addresses, string? countryCode, - List? categories, LocationType? locationType, - SpatialReference? outSpatialReference, RequestOptions? requestOptions) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode, categories, locationType, - outSpatialReference, requestOptions); - } - - /// - /// Find address candidates for multiple input addresses. - /// Uses the default ESRI geolocation service. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Additional options to be used for the data request - /// - /// - /// The name of the single line address field for the ArcGIS Locator Service (for ArcGIS 10+), defaults to 'address'. - /// - public async Task> AddressesToLocations(List addresses, string? countryCode, - List? categories, LocationType? locationType, - SpatialReference? outSpatialReference, RequestOptions? requestOptions, string? addressSearchStringParameterName) - { - return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode, categories, locationType, - outSpatialReference, requestOptions, addressSearchStringParameterName); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - public Task> AddressesToLocations(string url, List addresses) - { - return AddressesToLocations(url, addresses, null, null, null, null, null, null); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - public Task> AddressesToLocations(string url, List addresses, - string? countryCode) - { - return AddressesToLocations(url, addresses, countryCode, null, null, null, null, null); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - public Task> AddressesToLocations(string url, List addresses, - string? countryCode, List? categories) - { - return AddressesToLocations(url, addresses, countryCode, categories, null, null, null, null); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - public Task> AddressesToLocations(string url, List addresses, - string? countryCode, List? categories, LocationType? locationType) - { - return AddressesToLocations(url, addresses, countryCode, categories, locationType, null, null, null); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - public Task> AddressesToLocations(string url, List addresses, - string? countryCode, List? categories, LocationType? locationType, - SpatialReference? outSpatialReference) - { - return AddressesToLocations(url, addresses, countryCode, categories, locationType, outSpatialReference, null, - null); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Additional options to be used for the data request - /// - public Task> AddressesToLocations(string url, List addresses, - string? countryCode, List? categories, LocationType? locationType, - SpatialReference? outSpatialReference, RequestOptions? requestOptions) - { - return AddressesToLocations(url, addresses, countryCode, categories, locationType, outSpatialReference, - requestOptions, null); - } - - /// - /// Find address candidates for multiple input addresses. - /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// The input addresses in the format supported by the geocode service. - /// - /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only applies to the World Geocode Service. See the World Geocoding Service documentation for more information. - /// - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Additional options to be used for the data request - /// - /// - /// The name of the single line address field for the ArcGIS Locator Service (for ArcGIS 10+), defaults to 'address'. - /// - public Task> AddressesToLocations(string url, List addresses, - string? countryCode, List? categories, LocationType? locationType, - SpatialReference? outSpatialReference, RequestOptions? requestOptions, - string? addressSearchStringParameterName) - { - return AddressesToLocationsImplementation(url, addresses as object, countryCode, categories, locationType, - outSpatialReference, requestOptions, addressSearchStringParameterName); - } - -#endregion - - -#region AddressToLocationsWithAddress - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - [CodeGenerationIgnore] - public Task> AddressToLocations(Address address) - { - return AddressToLocations(ESRIGeoLocationUrl, address); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - public async Task> AddressToLocations(Address address, List? categories) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - public async Task> AddressToLocations(Address address, List? categories, - string? countryCode) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - public async Task> AddressToLocations(Address address, List? categories, - string? countryCode, bool? forStorage) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - public async Task> AddressToLocations(Address address, List? categories, - string? countryCode, bool? forStorage, Point? location) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - public async Task> AddressToLocations(Address address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - public async Task> AddressToLocations(Address address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType, - string? magicKey) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - public async Task> AddressToLocations(Address address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType, - string? magicKey, int? maxLocations) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - public async Task> AddressToLocations(Address address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType, - string? magicKey, int? maxLocations, List? outFields) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations, outFields); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - public async Task> AddressToLocations(Address address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType, - string? magicKey, int? maxLocations, List? outFields, - SpatialReference? outSpatialReference) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations, outFields, outSpatialReference); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. - /// - public async Task> AddressToLocations(Address address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType, - string? magicKey, int? maxLocations, List? outFields, - SpatialReference? outSpatialReference, Extent? searchExtent) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations, outFields, outSpatialReference, searchExtent); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. - /// - /// - /// Additional options to be used for the data request - /// - public async Task> AddressToLocations(Address address, List? categories = null, - string? countryCode = null, bool? forStorage = null, Point? location = null, LocationType? locationType = null, - string? magicKey = null, int? maxLocations = null, List? outFields = null, - SpatialReference? outSpatialReference = null, Extent? searchExtent = null, - RequestOptions? requestOptions = null) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations, outFields, outSpatialReference, searchExtent, requestOptions); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - public Task> AddressToLocations(string url, Address address) - { - return AddressToLocations(url, address, null, null, null, - null, null, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - public Task> AddressToLocations(string url, Address address, - List? categories) - { - return AddressToLocations(url, address, categories, null, null, - null, null, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - public Task> AddressToLocations(string url, Address address, - List? categories, string? countryCode) - { - return AddressToLocations(url, address, categories, countryCode, null, - null, null, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - public Task> AddressToLocations(string url, Address address, - List? categories, string? countryCode, bool? forStorage) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - null, null, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - public Task> AddressToLocations(string url, Address address, - List? categories, string? countryCode, bool? forStorage, Point? location) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, null, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - public Task> AddressToLocations(string url, Address address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - public Task> AddressToLocations(string url, Address address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, magicKey, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - public Task> AddressToLocations(string url, Address address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey, int? maxLocations) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, magicKey, maxLocations, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - public Task> AddressToLocations(string url, Address address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey, int? maxLocations, - List? outFields) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, magicKey, maxLocations, outFields, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - public Task> AddressToLocations(string url, Address address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey, int? maxLocations, - List? outFields, SpatialReference? outSpatialReference) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, magicKey, maxLocations, outFields, outSpatialReference, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. - /// - public Task> AddressToLocations(string url, Address address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey, int? maxLocations, - List? outFields, SpatialReference? outSpatialReference, Extent? searchExtent) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, magicKey, maxLocations, outFields, outSpatialReference, - searchExtent, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. - /// - /// - /// Additional options to be used for the data request - /// - /// -#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters - public async Task> AddressToLocations(string url, Address address, -#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey, int? maxLocations, - List? outFields, SpatialReference? outSpatialReference, Extent? searchExtent, - RequestOptions? requestOptions) - { - return await AddressToLocationsImplementation(url, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations, - outFields, outSpatialReference, searchExtent, requestOptions, null); - } - -#endregion - - -#region AddressToLocationsWithString - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - [CodeGenerationIgnore] - public Task> AddressToLocations(string address) - { - return AddressToLocations(ESRIGeoLocationUrl, address); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - public async Task> AddressToLocations(string address, List? categories) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - public async Task> AddressToLocations(string address, List? categories, - string? countryCode) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - public async Task> AddressToLocations(string address, List? categories, - string? countryCode, bool? forStorage) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - public async Task> AddressToLocations(string address, List? categories, - string? countryCode, bool? forStorage, Point? location) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - public async Task> AddressToLocations(string address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - public async Task> AddressToLocations(string address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType, - string? magicKey) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - public async Task> AddressToLocations(string address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType, - string? magicKey, int? maxLocations) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - public async Task> AddressToLocations(string address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType, - string? magicKey, int? maxLocations, List? outFields) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations, outFields); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - public async Task> AddressToLocations(string address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType, - string? magicKey, int? maxLocations, List? outFields, - SpatialReference? outSpatialReference) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations, outFields, outSpatialReference); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. - /// - public async Task> AddressToLocations(string address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType, - string? magicKey, int? maxLocations, List? outFields, - SpatialReference? outSpatialReference, Extent? searchExtent) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations, outFields, outSpatialReference, searchExtent); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// Uses the default ESRI geolocation service. - /// - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. - /// - /// - /// Additional options to be used for the data request - /// - /// - /// The name of the single line address field for the ArcGIS Locator Service (for ArcGIS 10+), defaults to 'address'. - /// - public async Task> AddressToLocations(string address, List? categories, - string? countryCode, bool? forStorage, Point? location, LocationType? locationType, - string? magicKey, int? maxLocations, List? outFields, - SpatialReference? outSpatialReference, Extent? searchExtent, - RequestOptions? requestOptions, string? addressSearchStringParameterName) - { - return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations, outFields, outSpatialReference, searchExtent, requestOptions, - addressSearchStringParameterName); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - public Task> AddressToLocations(string url, string address) - { - return AddressToLocations(url, address, null, null, null, - null, null, null, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - public Task> AddressToLocations(string url, string address, - List? categories) - { - return AddressToLocations(url, address, categories, null, null, - null, null, null, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - public Task> AddressToLocations(string url, string address, - List? categories, string? countryCode) - { - return AddressToLocations(url, address, categories, countryCode, null, - null, null, null, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - public Task> AddressToLocations(string url, string address, - List? categories, string? countryCode, bool? forStorage) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - null, null, null, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - public Task> AddressToLocations(string url, string address, - List? categories, string? countryCode, bool? forStorage, Point? location) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, null, null, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - public Task> AddressToLocations(string url, string address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, null, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - public Task> AddressToLocations(string url, string address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, magicKey, null, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - public Task> AddressToLocations(string url, string address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey, int? maxLocations) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, magicKey, maxLocations, null, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - public Task> AddressToLocations(string url, string address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey, int? maxLocations, - List? outFields) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, magicKey, maxLocations, outFields, null, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - public Task> AddressToLocations(string url, string address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey, int? maxLocations, - List? outFields, SpatialReference? outSpatialReference) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, magicKey, maxLocations, outFields, outSpatialReference, null, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. - /// - public Task> AddressToLocations(string url, string address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey, int? maxLocations, - List? outFields, SpatialReference? outSpatialReference, Extent? searchExtent) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, - location, locationType, magicKey, maxLocations, outFields, outSpatialReference, - searchExtent, null, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. - /// - /// - /// Additional options to be used for the data request - /// - public Task> AddressToLocations(string url, string address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey, int? maxLocations, - List? outFields, SpatialReference? outSpatialReference, Extent? searchExtent, - RequestOptions? requestOptions) - { - return AddressToLocations(url, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations, - outFields, outSpatialReference, searchExtent, requestOptions, null); - } - - /// - /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the address parameter. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// the various address fields accepted by the corresponding geocode service. - /// - /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". - /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. - /// - /// - /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. - /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. - /// - /// Allows the results of single geocode transactions to be persisted. - /// Used to weight returned results for a specified area. - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// A suggestLocations result ID (magicKey). Used to query for a specific results information. - /// Maximum results to return from the query. - /// - /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection candidate fields. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. - /// - /// - /// Additional options to be used for the data request - /// - /// - /// The name of the single line address field for the ArcGIS Locator Service (for ArcGIS 10+), defaults to 'address'. - /// - public Task> AddressToLocations(string url, string address, - List? categories, string? countryCode, bool? forStorage, Point? location, - LocationType? locationType, string? magicKey, int? maxLocations, - List? outFields, SpatialReference? outSpatialReference, Extent? searchExtent, - RequestOptions? requestOptions, string? addressSearchStringParameterName) - { - return AddressToLocationsImplementation(url, address, categories, countryCode, forStorage, location, - locationType, magicKey, maxLocations, - outFields, outSpatialReference, searchExtent, requestOptions, addressSearchStringParameterName); - } - -#endregion - - -#region LocationToAddress - - /// - /// Locates an address based on a given point. - /// Uses the default ESRI geolocation service. - /// - /// - /// The point at which to search for the closest address. The location should be in the same spatial reference as that of the geocode service. - /// - [CodeGenerationIgnore] - public Task LocationToAddress(Point location) - { - return LocationToAddress(ESRIGeoLocationUrl, location); - } - - /// - /// Locates an address based on a given point. - /// Uses the default ESRI geolocation service. - /// - /// - /// The point at which to search for the closest address. The location should be in the same spatial reference as that of the geocode service. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - public Task LocationToAddress(Point location, LocationType? locationType) - { - return LocationToAddress(ESRIGeoLocationUrl, location, locationType); - } - - /// - /// Locates an address based on a given point. - /// Uses the default ESRI geolocation service. - /// - /// - /// The point at which to search for the closest address. The location should be in the same spatial reference as that of the geocode service. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - public Task LocationToAddress(Point location, LocationType? locationType, - SpatialReference? outSpatialReference) - { - return LocationToAddress(ESRIGeoLocationUrl, location, locationType, outSpatialReference); - } - - /// - /// Locates an address based on a given point. - /// Uses the default ESRI geolocation service. - /// - /// - /// The point at which to search for the closest address. The location should be in the same spatial reference as that of the geocode service. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Additional options to be used for the data request - /// - public Task LocationToAddress(Point location, LocationType? locationType, - SpatialReference? outSpatialReference, RequestOptions? requestOptions) - { - return LocationToAddress(ESRIGeoLocationUrl, location, locationType, outSpatialReference, requestOptions); - } - - /// - /// Locates an address based on a given point. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// - /// The point at which to search for the closest address. The location should be in the same spatial reference as that of the geocode service. - /// - public Task LocationToAddress(string url, Point location) - { - return InvokeAsync("locationToAddress", url, location, null, null, null); - } - - /// - /// Locates an address based on a given point. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// - /// The point at which to search for the closest address. The location should be in the same spatial reference as that of the geocode service. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - public Task LocationToAddress(string url, Point location, LocationType? locationType) - { - return InvokeAsync("locationToAddress", url, location, locationType, null, null); - } - - /// - /// Locates an address based on a given point. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// - /// The point at which to search for the closest address. The location should be in the same spatial reference as that of the geocode service. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - public Task LocationToAddress(string url, Point location, LocationType? locationType, - SpatialReference? outSpatialReference) - { - return LocationToAddress(url, location, locationType, outSpatialReference, null); - } - - /// - /// Locates an address based on a given point. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// - /// The point at which to search for the closest address. The location should be in the same spatial reference as that of the geocode service. - /// - /// - /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. - /// - /// - /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial reference of the input geometries when performing a reverse geocode and in the default spatial reference returned by the service if finding locations by address. - /// - /// - /// Additional options to be used for the data request - /// - public async Task LocationToAddress(string url, Point location, LocationType? locationType, - SpatialReference? outSpatialReference, RequestOptions? requestOptions) - { - return await InvokeAsync("locationToAddress", url, location, locationType, - outSpatialReference, requestOptions); - } - -#endregion - - -#region SuggestLocations - - /// - /// Get character by character auto complete suggestions. - /// Uses the default ESRI geolocation service. - /// - /// - /// Defines a normalized location point that is used to sort geocoding candidates based upon their proximity to the given location. - /// - /// - /// The input text entered by a user which is used by the suggest operation to generate a list of possible matches. - /// - [CodeGenerationIgnore] - public async Task> SuggestLocations(Point location, string text) - { - return await SuggestLocations(ESRIGeoLocationUrl, location, text); - } - - /// - /// Get character by character auto complete suggestions. - /// Uses the default ESRI geolocation service. - /// - /// - /// Defines a normalized location point that is used to sort geocoding candidates based upon their proximity to the given location. - /// - /// - /// The input text entered by a user which is used by the suggest operation to generate a list of possible matches. - /// - /// - /// A place or address type which can be used to filter suggest results. The parameter supports input of single category values or multiple comma-separated values. - /// - public async Task> SuggestLocations(Point location, string text, - List? categories) - { - return await SuggestLocations(ESRIGeoLocationUrl, location, text, categories); - } - - /// - /// Get character by character auto complete suggestions. - /// Uses the default ESRI geolocation service. - /// - /// - /// Defines a normalized location point that is used to sort geocoding candidates based upon their proximity to the given location. - /// - /// - /// The input text entered by a user which is used by the suggest operation to generate a list of possible matches. - /// - /// - /// A place or address type which can be used to filter suggest results. The parameter supports input of single category values or multiple comma-separated values. - /// - /// - /// Additional options to be used for the data request - /// - public async Task> SuggestLocations(Point location, string text, - List? categories, RequestOptions? requestOptions) - { - return await SuggestLocations(ESRIGeoLocationUrl, location, text, categories, requestOptions); - } - - /// - /// Get character by character auto complete suggestions. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// - /// Defines a normalized location point that is used to sort geocoding candidates based upon their proximity to the given location. - /// - /// - /// The input text entered by a user which is used by the suggest operation to generate a list of possible matches. - /// - public async Task> SuggestLocations(string url, Point location, string text) - { - return await InvokeAsync>("suggestLocations", url, location, text, null, null); - } - - /// - /// Get character by character auto complete suggestions. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// - /// Defines a normalized location point that is used to sort geocoding candidates based upon their proximity to the given location. - /// - /// - /// The input text entered by a user which is used by the suggest operation to generate a list of possible matches. - /// - /// - /// A place or address type which can be used to filter suggest results. The parameter supports input of single category values or multiple comma-separated values. - /// - public async Task> SuggestLocations(string url, Point location, string text, - List? categories) - { - return await InvokeAsync>("suggestLocations", url, location, text, - categories, null); - } - - /// - /// Get character by character auto complete suggestions. - /// - /// URL to the ArcGIS Server REST resource that represents a locator service. - /// - /// Defines a normalized location point that is used to sort geocoding candidates based upon their proximity to the given location. - /// - /// - /// The input text entered by a user which is used by the suggest operation to generate a list of possible matches. - /// - /// - /// A place or address type which can be used to filter suggest results. The parameter supports input of single category values or multiple comma-separated values. - /// - /// - /// Additional options to be used for the data request - /// - public async Task> SuggestLocations(string url, Point location, string text, - List? categories, RequestOptions? requestOptions) - { - return await InvokeAsync>("suggestLocations", url, location, text, categories, - requestOptions); - } - -#endregion -} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Components/LocationService.gb.cs b/src/dymaptic.GeoBlazor.Core/Components/LocationService.gb.cs deleted file mode 100644 index da0f6c326..000000000 --- a/src/dymaptic.GeoBlazor.Core/Components/LocationService.gb.cs +++ /dev/null @@ -1,15 +0,0 @@ -// File auto-generated by dymaptic tooling. Any changes made here will be lost on future generation. To override functionality, use the relevant root .cs file. - -namespace dymaptic.GeoBlazor.Core.Components; - - -/// -/// GeoBlazor Docs -/// A convenience module for importing locator functions when developing with -/// TypeScript. -/// ArcGIS Maps SDK for JavaScript -/// -public partial class LocationService -{ - -} diff --git a/src/dymaptic.GeoBlazor.Core/Components/MapComponent.razor.cs b/src/dymaptic.GeoBlazor.Core/Components/MapComponent.razor.cs index e3dbaf9e6..587f6ae31 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/MapComponent.razor.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/MapComponent.razor.cs @@ -66,13 +66,6 @@ public JsModuleManager? JsModuleManager [JsonIgnore] public bool MapRendered { get; set; } - /// - /// The reference to geoBlazorCore.ts from .NET - /// - [Obsolete("Use the CoreJsModule property instead.")] - [JsonIgnore] - public IJSObjectReference? JsModule => CoreJsModule; - /// /// The reference to the entry point geoBlazorCore.js from .NET /// @@ -185,7 +178,7 @@ public Guid? ViewId /// /// Boolean flag to identify if GeoBlazor is running in Blazor Hybrid (MAUI) mode /// - protected internal bool IsMaui => JsRuntime!.GetType().Name.Contains("WebView"); + protected internal bool IsMaui => JsRuntime?.GetType().Name.Contains("WebView") ?? false; /// /// Implements the `IAsyncDisposable` pattern. @@ -802,7 +795,8 @@ public Task SetVisible(bool visible) try { - if (await jsonStreamReference.ReadJsStreamReferenceAsJSON(MapComponentType) is MapComponent deserialized) + if (await jsonStreamReference.ReadJsStreamReferenceAsJSON(MapComponentType, View?.QueryResultsMaxSizeLimit) + is MapComponent deserialized) { if (IsDisposed) { @@ -922,11 +916,15 @@ protected virtual async Task RenderView(bool forceRender = false) /// /// Retrieves the reflected public and private instance properties of the current type /// - protected PropertyInfo[] GetPropertyInfos() + protected internal PropertyInfo[] GetPropertyInfos() { return MapComponentType - .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - .Where(p => p.DeclaringType?.Namespace?.StartsWith("dymaptic.GeoBlazor") == true) + .GetProperties(BindingFlags.Public | BindingFlags.NonPublic + | BindingFlags.Instance) + .Where(p => p.SetMethod is not null + && p.DeclaringType?.Namespace?.StartsWith("dymaptic.GeoBlazor") == true + && !excludedProps.Contains(p.Name) + && p.GetCustomAttribute() is null) .ToArray(); } @@ -1138,7 +1136,8 @@ protected internal void UpdateGeoBlazorReferences(IJSObjectReference coreJsModul foreach (PropertyInfo prop in _props) { - if (_circularMapComponents.Contains(prop.Name) + if (prop.SetMethod is null + || _circularMapComponents.Contains(prop.Name) || _circularMapComponents.Contains(prop.PropertyType.Name) || (prop.PropertyType.IsGenericType && _circularMapComponents.Any(c => prop.PropertyType @@ -1202,6 +1201,13 @@ protected internal void UpdateGeoBlazorReferences(IJSObjectReference coreJsModul } } + private static readonly string[] excludedProps = + [ + nameof(ChildContent), + nameof(DotNetComponentReference), + nameof(IsDisposed) + ]; + private static Type? _proExtensions; private readonly Dictionary _watchers = new(); @@ -2038,7 +2044,6 @@ internal class MapComponentConverter : JsonConverter public override void Write(Utf8JsonWriter writer, MapComponent value, JsonSerializerOptions options) { - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), - GeoBlazorSerialization.JsonSerializerOptions)); + JsonSerializer.Serialize(writer, value, value.GetType(), options); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Components/MediaInfo.cs b/src/dymaptic.GeoBlazor.Core/Components/MediaInfo.cs index ae1b3d94a..ba493062f 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/MediaInfo.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/MediaInfo.cs @@ -40,10 +40,6 @@ internal class MediaInfoConverter : JsonConverter public override void Write(Utf8JsonWriter writer, MediaInfo value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), newOptions)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Components/OpacityVariable.gb.cs b/src/dymaptic.GeoBlazor.Core/Components/OpacityVariable.gb.cs index af43156eb..a5f969b2e 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/OpacityVariable.gb.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/OpacityVariable.gb.cs @@ -50,7 +50,7 @@ public OpacityVariable() /// Arcade expression as defined in the valueExpression property. /// ArcGIS Maps SDK for JavaScript /// - public OpacityVariable(string field, + public OpacityVariable(string? field, string? normalizationField = null, IReadOnlyList? stops = null, VisualVariableLegendOptions? legendOptions = null, diff --git a/src/dymaptic.GeoBlazor.Core/Components/Popups/PopupContent.cs b/src/dymaptic.GeoBlazor.Core/Components/Popups/PopupContent.cs index f3d931eb0..e445e5ec7 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/Popups/PopupContent.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/Popups/PopupContent.cs @@ -56,12 +56,14 @@ internal class PopupContentConverter : JsonConverter break; case "custom": // CustomPopupContent is in GeoBlazor Pro assembly, so we need to use reflection to get the type - Type? customType = Type.GetType("dymaptic.GeoBlazor.Pro.Components.Popups.CustomPopupContent, dymaptic.GeoBlazor.Pro"); + Type? customType = + Type.GetType("dymaptic.GeoBlazor.Pro.Components.Popups.CustomPopupContent, dymaptic.GeoBlazor.Pro"); if (customType is not null && customType.IsSubclassOf(typeof(PopupContent))) { content = - JsonSerializer.Deserialize(jsonDoc.RootElement.GetRawText(), customType, options) as PopupContent; + JsonSerializer.Deserialize(jsonDoc.RootElement.GetRawText(), customType, options) as + PopupContent; } break; @@ -72,10 +74,6 @@ internal class PopupContentConverter : JsonConverter public override void Write(Utf8JsonWriter writer, PopupContent value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), newOptions)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Components/Portal.gb.cs b/src/dymaptic.GeoBlazor.Core/Components/Portal.gb.cs index 6ad26389b..da3d48322 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/Portal.gb.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/Portal.gb.cs @@ -427,15 +427,6 @@ public Portal(PortalAccess? access = null, public override void ValidateRequiredGeneratedChildren() { DefaultExtent?.ValidateRequiredGeneratedChildren(); - - if (FeaturedGroups is not null) - { - foreach (PortalFeaturedGroups child in FeaturedGroups) - { - child.ValidateRequiredGeneratedChildren(); - } - } - User?.ValidateRequiredGeneratedChildren(); base.ValidateRequiredGeneratedChildren(); } @@ -452,16 +443,6 @@ protected override async ValueTask RegisterGeneratedChildComponent(MapComp ModifiedParameters[nameof(DefaultExtent)] = DefaultExtent; } - return true; - case PortalFeaturedGroups featuredGroups: - FeaturedGroups ??= []; - - if (!FeaturedGroups.Contains(featuredGroups)) - { - FeaturedGroups = [..FeaturedGroups, featuredGroups]; - ModifiedParameters[nameof(FeaturedGroups)] = FeaturedGroups; - } - return true; case PortalUser user: if (user != User) @@ -485,11 +466,6 @@ protected override async ValueTask UnregisterGeneratedChildComponent(MapCo DefaultExtent = null; ModifiedParameters[nameof(DefaultExtent)] = DefaultExtent; - return true; - case PortalFeaturedGroups featuredGroups: - FeaturedGroups = FeaturedGroups?.Where(f => f != featuredGroups).ToList(); - ModifiedParameters[nameof(FeaturedGroups)] = FeaturedGroups; - return true; case PortalUser _: User = null; @@ -4910,51 +4886,6 @@ await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, JsComponentReference, "eueiEnabled", value); } - /// - /// Asynchronously set the value of the FeaturedGroups property after render. - /// - /// - /// The value to set. - /// - public async Task SetFeaturedGroups(IReadOnlyList? value) - { - if (value is not null) - { - foreach (PortalFeaturedGroups item in value) - { - item.UpdateGeoBlazorReferences(CoreJsModule!, ProJsModule, View, this, Layer); - } - } - -#pragma warning disable BL0005 - FeaturedGroups = value; -#pragma warning restore BL0005 - ModifiedParameters[nameof(FeaturedGroups)] = value; - - if (CoreJsModule is null) - { - return; - } - - try - { - JsComponentReference ??= await CoreJsModule.InvokeAsync( - "getJsComponent", CancellationTokenSource.Token, Id); - } - catch (JSException) - { - // this is expected if the component is not yet built - } - - if (JsComponentReference is null) - { - return; - } - - await JsComponentReference.InvokeVoidAsync("setFeaturedGroups", - CancellationTokenSource.Token, value); - } - /// /// Asynchronously set the value of the FeaturedItemsGroupQuery property after render. /// @@ -6269,20 +6200,6 @@ public async Task AddToAuthorizedCrossOriginDomains(params string[] values) await SetAuthorizedCrossOriginDomains(join); } - /// - /// Asynchronously adds elements to the FeaturedGroups property. - /// - /// - /// The elements to add. - /// - public async Task AddToFeaturedGroups(params PortalFeaturedGroups[] values) - { - PortalFeaturedGroups[] join = FeaturedGroups is null - ? values - : [..FeaturedGroups, ..values]; - await SetFeaturedGroups(join); - } - /// /// Asynchronously adds elements to the RotatorPanels property. /// @@ -6318,22 +6235,6 @@ public async Task RemoveFromAuthorizedCrossOriginDomains(params string[] values) await SetAuthorizedCrossOriginDomains(AuthorizedCrossOriginDomains.Except(values).ToArray()); } - /// - /// Asynchronously remove an element from the FeaturedGroups property. - /// - /// - /// The elements to remove. - /// - public async Task RemoveFromFeaturedGroups(params PortalFeaturedGroups[] values) - { - if (FeaturedGroups is null) - { - return; - } - - await SetFeaturedGroups(FeaturedGroups.Except(values).ToArray()); - } - /// /// Asynchronously remove an element from the RotatorPanels property. /// diff --git a/src/dymaptic.GeoBlazor.Core/Components/PortalFeaturedGroups.cs b/src/dymaptic.GeoBlazor.Core/Components/PortalFeaturedGroups.cs deleted file mode 100644 index 3ade21e31..000000000 --- a/src/dymaptic.GeoBlazor.Core/Components/PortalFeaturedGroups.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace dymaptic.GeoBlazor.Core.Components; - -public partial class PortalFeaturedGroups -{ - // Add custom code to this file to override generated code -} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Components/PortalFeaturedGroups.gb.cs b/src/dymaptic.GeoBlazor.Core/Components/PortalFeaturedGroups.gb.cs deleted file mode 100644 index ccf1fb289..000000000 --- a/src/dymaptic.GeoBlazor.Core/Components/PortalFeaturedGroups.gb.cs +++ /dev/null @@ -1,229 +0,0 @@ -// File auto-generated by dymaptic tooling. Any changes made here will be lost on future generation. To override functionality, use the relevant root .cs file. - -namespace dymaptic.GeoBlazor.Core.Components; - - -/// -/// GeoBlazor Docs -/// The featured groups for the portal. -/// ArcGIS Maps SDK for JavaScript -/// -public partial class PortalFeaturedGroups : MapComponent -{ - - /// - /// Parameterless constructor for use as a Razor Component. - /// - [ActivatorUtilitiesConstructor] - public PortalFeaturedGroups() - { - } - - /// - /// Constructor for use in C# code. Use named parameters (e.g., item1: value1, item2: value2) to set properties in any order. - /// - /// - /// Name of the group owner. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// Group title. - /// ArcGIS Maps SDK for JavaScript - /// - public PortalFeaturedGroups( - string? owner = null, - string? title = null) - { - AllowRender = false; -#pragma warning disable BL0005 - Owner = owner; - Title = title; -#pragma warning restore BL0005 - } - - -#region Public Properties / Blazor Parameters - - /// - /// GeoBlazor Docs - /// Name of the group owner. - /// ArcGIS Maps SDK for JavaScript - /// - [ArcGISProperty] - [Parameter] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Owner { get; set; } - - /// - /// GeoBlazor Docs - /// Group title. - /// ArcGIS Maps SDK for JavaScript - /// - [ArcGISProperty] - [Parameter] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Title { get; set; } - -#endregion - -#region Property Getters - - /// - /// Asynchronously retrieve the current value of the Owner property. - /// - public async Task GetOwner() - { - if (CoreJsModule is null) - { - return Owner; - } - - try - { - JsComponentReference ??= await CoreJsModule.InvokeAsync( - "getJsComponent", CancellationTokenSource.Token, Id); - } - catch (JSException) - { - // this is expected if the component is not yet built - } - - if (JsComponentReference is null) - { - return Owner; - } - - // get the property value - string? result = await JsComponentReference!.InvokeAsync("getProperty", - CancellationTokenSource.Token, "owner"); - if (result is not null) - { -#pragma warning disable BL0005 - Owner = result; -#pragma warning restore BL0005 - ModifiedParameters[nameof(Owner)] = Owner; - } - - return Owner; - } - - /// - /// Asynchronously retrieve the current value of the Title property. - /// - public async Task GetTitle() - { - if (CoreJsModule is null) - { - return Title; - } - - try - { - JsComponentReference ??= await CoreJsModule.InvokeAsync( - "getJsComponent", CancellationTokenSource.Token, Id); - } - catch (JSException) - { - // this is expected if the component is not yet built - } - - if (JsComponentReference is null) - { - return Title; - } - - // get the property value - string? result = await JsComponentReference!.InvokeAsync("getProperty", - CancellationTokenSource.Token, "title"); - if (result is not null) - { -#pragma warning disable BL0005 - Title = result; -#pragma warning restore BL0005 - ModifiedParameters[nameof(Title)] = Title; - } - - return Title; - } - -#endregion - -#region Property Setters - - /// - /// Asynchronously set the value of the Owner property after render. - /// - /// - /// The value to set. - /// - public async Task SetOwner(string? value) - { -#pragma warning disable BL0005 - Owner = value; -#pragma warning restore BL0005 - ModifiedParameters[nameof(Owner)] = value; - - if (CoreJsModule is null) - { - return; - } - - try - { - JsComponentReference ??= await CoreJsModule.InvokeAsync( - "getJsComponent", CancellationTokenSource.Token, Id); - } - catch (JSException) - { - // this is expected if the component is not yet built - } - - if (JsComponentReference is null) - { - return; - } - - await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, - JsComponentReference, "owner", value); - } - - /// - /// Asynchronously set the value of the Title property after render. - /// - /// - /// The value to set. - /// - public async Task SetTitle(string? value) - { -#pragma warning disable BL0005 - Title = value; -#pragma warning restore BL0005 - ModifiedParameters[nameof(Title)] = value; - - if (CoreJsModule is null) - { - return; - } - - try - { - JsComponentReference ??= await CoreJsModule.InvokeAsync( - "getJsComponent", CancellationTokenSource.Token, Id); - } - catch (JSException) - { - // this is expected if the component is not yet built - } - - if (JsComponentReference is null) - { - return; - } - - await CoreJsModule.InvokeVoidAsync("setProperty", CancellationTokenSource.Token, - JsComponentReference, "title", value); - } - -#endregion - -} diff --git a/src/dymaptic.GeoBlazor.Core/Components/RotationVariable.gb.cs b/src/dymaptic.GeoBlazor.Core/Components/RotationVariable.gb.cs index 86a5486f1..881e8a2dd 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/RotationVariable.gb.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/RotationVariable.gb.cs @@ -55,7 +55,7 @@ public RotationVariable() /// Arcade expression as defined in the valueExpression property. /// ArcGIS Maps SDK for JavaScript /// - public RotationVariable(string field, + public RotationVariable(string? field, Axis? axis = null, RotationType? rotationType = null, VisualVariableLegendOptions? legendOptions = null, diff --git a/src/dymaptic.GeoBlazor.Core/Components/SizeVariable.cs b/src/dymaptic.GeoBlazor.Core/Components/SizeVariable.cs index db3d61bbf..9fbef123f 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/SizeVariable.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/SizeVariable.cs @@ -1,8 +1,129 @@ namespace dymaptic.GeoBlazor.Core.Components; +[JsonConverter(typeof(SizeVariableConverter))] public partial class SizeVariable : VisualVariable, IMapComponent { + /// + /// Parameterless constructor for use as a Razor Component. + /// + [ActivatorUtilitiesConstructor] + [CodeGenerationIgnore] + public SizeVariable() + { + } + /// + /// Constructor for use in C# code. Use named parameters (e.g., item1: value1, item2: value2) to set properties in any order. + /// + /// + /// The name of the numeric attribute field that contains the data + /// values used to determine the color/opacity/size/rotation of each feature. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The size used to render a feature containing the minimum data value. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The size used to render a feature containing the maximum data value. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The minimum data value used in the size ramp. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The maximum data value used in the size ramp. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// Specifies how to apply the data value when mapping real-world sizes. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// Indicates the unit of measurement used to interpret the value returned by field or valueExpression. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The name of the numeric attribute field used to normalize + /// the data in the given field. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// This value must be `outline` when scaling polygon outline widths + /// based on the view scale. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// When setting a size visual variable on a renderer using an + /// ObjectSymbol3DLayer, this property indicates whether to apply the value + /// defined by the height, + /// width, or + /// depth properties to the corresponding axis of + /// this visual variable instead of proportionally scaling this axis' value. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// Only applicable when working in a SceneView. + /// default "all" + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// An Arcade expression following the specification + /// defined by the Arcade Visualization Profile. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// The title identifying and describing the associated + /// Arcade expression as defined in the valueExpression property. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// An object providing options for displaying the visual variable in + /// the Legend. + /// ArcGIS Maps SDK for JavaScript + /// + /// + /// An array of objects that defines the mapping of data values returned from field or + /// valueExpression to icon sizes. + /// ArcGIS Maps SDK for JavaScript + /// + [CodeGenerationIgnore] + public SizeVariable(string? field, + Dimension? minSize = null, + Dimension? maxSize = null, + double? minDataValue = null, + double? maxDataValue = null, + VisualValueRepresentation? valueRepresentation = null, + VisualValueUnit? valueUnit = null, + string? normalizationField = null, + string? target = null, + bool? useSymbolValue = null, + VisualAxis? axis = null, + string? valueExpression = null, + string? valueExpressionTitle = null, + VisualVariableLegendOptions? legendOptions = null, + IReadOnlyList? stops = null) + { + AllowRender = false; +#pragma warning disable BL0005 + Field = field; + MinSize = minSize; + MaxSize = maxSize; + MinDataValue = minDataValue; + MaxDataValue = maxDataValue; + ValueRepresentation = valueRepresentation; + ValueUnit = valueUnit; + NormalizationField = normalizationField; + Target = target; + UseSymbolValue = useSymbolValue; + Axis = axis; + ValueExpression = valueExpression; + ValueExpressionTitle = valueExpressionTitle; + LegendOptions = legendOptions; + Stops = stops; +#pragma warning restore BL0005 + } /// public override VisualVariableType Type => VisualVariableType.Size; @@ -13,7 +134,7 @@ public partial class SizeVariable : VisualVariable, IMapComponent [Parameter] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VisualAxis? Axis { get; set; } - + /// /// The minimum data value used in the size ramp. /// @@ -35,6 +156,7 @@ public partial class SizeVariable : VisualVariable, IMapComponent /// [Parameter] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [CodeGenerationIgnore] public Dimension? MinSize { get; set; } /// @@ -44,36 +166,55 @@ public partial class SizeVariable : VisualVariable, IMapComponent /// [Parameter] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [CodeGenerationIgnore] public Dimension? MaxSize { get; set; } - + + /// + /// The size used to render a feature containing the minimum data value. + /// When a SizeVariable is used, the size of features whose data value (defined in field or valueExpression) is greater than or equal to the minDataValue for the given view scale. + /// + [Parameter] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [CodeGenerationIgnore] + public SizeVariable? MinSizeVariable { get; set; } + + /// + /// The size used to render a feature containing the maximum data value. + /// When a SizeVariable is used, the size of features whose data value (defined in field or valueExpression) is greater than or equal to the maxDataValue for the given view scale. + /// + [Parameter] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [CodeGenerationIgnore] + public SizeVariable? MaxSizeVariable { get; set; } + /// /// The name of the numeric attribute field used to normalize the data in the given field. If this field is used, then the values in maxDataValue and minDataValue or stops should be normalized as percentages or ratios. /// [Parameter] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? NormalizationField { get; set; } - + /// /// This value must be outline when scaling polygon outline widths based on the view scale. If scale-dependent icons are desired, then this property should be ignored. /// [Parameter] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Target { get; set; } - + /// /// When setting a size visual variable on a renderer using an ObjectSymbol3DLayer, this property indicates whether to apply the value defined by the height, width, or depth properties to the corresponding axis of this visual variable instead of proportionally scaling this axis' value. /// [Parameter] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool? UseSymbolValue { get; set; } - + /// /// Specifies how to apply the data value when mapping real-world sizes. /// [Parameter] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VisualValueRepresentation? ValueRepresentation { get; set; } - + /// /// Indicates the unit of measurement used to interpret the value returned by field or valueExpression. For 3D volumetric symbols the default is meters. This property should not be used if the data value represents a thematic quantity (e.g. traffic count, census data, etc.). /// @@ -81,7 +222,6 @@ public partial class SizeVariable : VisualVariable, IMapComponent [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VisualValueUnit? ValueUnit { get; set; } - /// public override async Task RegisterChildComponent(MapComponent child) { @@ -98,7 +238,7 @@ public override async Task RegisterChildComponent(MapComponent child) break; } } - + /// public override async Task UnregisterChildComponent(MapComponent child) { @@ -114,5 +254,4 @@ public override async Task UnregisterChildComponent(MapComponent child) break; } } - } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Components/SizeVariable.gb.cs b/src/dymaptic.GeoBlazor.Core/Components/SizeVariable.gb.cs index 4969a056f..6d9e8e03c 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/SizeVariable.gb.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/SizeVariable.gb.cs @@ -10,127 +10,6 @@ namespace dymaptic.GeoBlazor.Core.Components; ///
public partial class SizeVariable { - /// - /// Parameterless constructor for use as a Razor Component. - /// - [ActivatorUtilitiesConstructor] - public SizeVariable() - { - } - - /// - /// Constructor for use in C# code. Use named parameters (e.g., item1: value1, item2: value2) to set properties in any order. - /// - /// - /// The name of the numeric attribute field that contains the data - /// values used to determine the color/opacity/size/rotation of each feature. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The size used to render a feature containing the minimum data value. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The size used to render a feature containing the maximum data value. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The minimum data value used in the size ramp. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The maximum data value used in the size ramp. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// Specifies how to apply the data value when mapping real-world sizes. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// Indicates the unit of measurement used to interpret the value returned by field or valueExpression. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The name of the numeric attribute field used to normalize - /// the data in the given field. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// This value must be `outline` when scaling polygon outline widths - /// based on the view scale. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// When setting a size visual variable on a renderer using an - /// ObjectSymbol3DLayer, this property indicates whether to apply the value - /// defined by the height, - /// width, or - /// depth properties to the corresponding axis of - /// this visual variable instead of proportionally scaling this axis' value. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// Only applicable when working in a SceneView. - /// default "all" - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// An Arcade expression following the specification - /// defined by the Arcade Visualization Profile. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// The title identifying and describing the associated - /// Arcade expression as defined in the valueExpression property. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// An object providing options for displaying the visual variable in - /// the Legend. - /// ArcGIS Maps SDK for JavaScript - /// - /// - /// An array of objects that defines the mapping of data values returned from field or - /// valueExpression to icon sizes. - /// ArcGIS Maps SDK for JavaScript - /// - public SizeVariable(string field, - Dimension? minSize = null, - Dimension? maxSize = null, - double? minDataValue = null, - double? maxDataValue = null, - VisualValueRepresentation? valueRepresentation = null, - VisualValueUnit? valueUnit = null, - string? normalizationField = null, - string? target = null, - bool? useSymbolValue = null, - VisualAxis? axis = null, - string? valueExpression = null, - string? valueExpressionTitle = null, - VisualVariableLegendOptions? legendOptions = null, - IReadOnlyList? stops = null) - { - AllowRender = false; -#pragma warning disable BL0005 - Field = field; - MinSize = minSize; - MaxSize = maxSize; - MinDataValue = minDataValue; - MaxDataValue = maxDataValue; - ValueRepresentation = valueRepresentation; - ValueUnit = valueUnit; - NormalizationField = normalizationField; - Target = target; - UseSymbolValue = useSymbolValue; - Axis = axis; - ValueExpression = valueExpression; - ValueExpressionTitle = valueExpressionTitle; - LegendOptions = legendOptions; - Stops = stops; -#pragma warning restore BL0005 - } - - #region Public Properties / Blazor Parameters /// diff --git a/src/dymaptic.GeoBlazor.Core/Components/Symbols/Outline.cs b/src/dymaptic.GeoBlazor.Core/Components/Symbols/Outline.cs index 7a6b3eab1..5b53a9d28 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/Symbols/Outline.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/Symbols/Outline.cs @@ -65,7 +65,7 @@ public Outline(MapColor? color = null, /// /// Convenience constructor for creating an outline from a . /// - public Outline(SimpleLineSymbol symbol): base(symbol.Color, symbol.Width, symbol.Style, symbol.Cap, symbol.Join, + public Outline(SimpleLineSymbol symbol) : base(symbol.Color, symbol.Width, symbol.Style, symbol.Cap, symbol.Join, symbol.Marker, symbol.MiterLimit) { AllowRender = false; @@ -81,10 +81,6 @@ internal class OutlineConverter : JsonConverter public override void Write(Utf8JsonWriter writer, Outline value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), newOptions)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Components/Views/MapView.razor b/src/dymaptic.GeoBlazor.Core/Components/Views/MapView.razor index 8364cbf42..eb40d853d 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/Views/MapView.razor +++ b/src/dymaptic.GeoBlazor.Core/Components/Views/MapView.razor @@ -6,7 +6,10 @@
@if (MapComponentType == typeof(SceneView)) { - + @if (!string.IsNullOrWhiteSpace(ErrorMessage)) {
@@ -28,7 +31,10 @@ } else { - + @if (!string.IsNullOrWhiteSpace(ErrorMessage)) {
diff --git a/src/dymaptic.GeoBlazor.Core/Components/VisualVariable.gb.cs b/src/dymaptic.GeoBlazor.Core/Components/VisualVariable.gb.cs index dcc44fbdd..eb732fc46 100644 --- a/src/dymaptic.GeoBlazor.Core/Components/VisualVariable.gb.cs +++ b/src/dymaptic.GeoBlazor.Core/Components/VisualVariable.gb.cs @@ -12,11 +12,6 @@ public abstract partial class VisualVariable /// public override void ValidateRequiredGeneratedChildren() { - if (Field is null) - { - throw new MissingRequiredChildElementException(nameof(VisualVariable), nameof(Field)); - } - LegendOptions?.ValidateRequiredGeneratedChildren(); base.ValidateRequiredGeneratedChildren(); } @@ -65,9 +60,8 @@ protected override async ValueTask UnregisterGeneratedChildComponent(MapCo ///
[ArcGISProperty] [Parameter] - [RequiredProperty] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string Field { get; set; } = null!; + public string? Field { get; set; } /// /// GeoBlazor Docs diff --git a/src/dymaptic.GeoBlazor.Core/DependencyExtension.cs b/src/dymaptic.GeoBlazor.Core/DependencyExtension.cs index 280167afb..c851f9b96 100644 --- a/src/dymaptic.GeoBlazor.Core/DependencyExtension.cs +++ b/src/dymaptic.GeoBlazor.Core/DependencyExtension.cs @@ -11,10 +11,11 @@ public static class DependencyExtension public static IServiceCollection AddGeoBlazor(this IServiceCollection serviceCollection, IConfiguration? configuration) { - GeoBlazorSettings settings = configuration?.GetSection("GeoBlazor").Get() + GeoBlazorSettings settings = configuration?.GetSection("GeoBlazor").Get() ?? new GeoBlazorSettings(); RegisterSerializationData(); + return serviceCollection .AddSingleton(_ => settings) .AddScoped() diff --git a/src/dymaptic.GeoBlazor.Core/Enums/BasemapStyleName.cs b/src/dymaptic.GeoBlazor.Core/Enums/BasemapStyleName.cs index 8e1f62623..481afbad6 100644 --- a/src/dymaptic.GeoBlazor.Core/Enums/BasemapStyleName.cs +++ b/src/dymaptic.GeoBlazor.Core/Enums/BasemapStyleName.cs @@ -131,19 +131,22 @@ public enum BasemapStyleName #pragma warning restore 1591 } -internal class EnumMemberConverter : JsonConverter where T: struct, IConvertible +internal class EnumMemberConverter : JsonConverter where T : struct, IConvertible { public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { string? enumMemberValue = reader.GetString(); + foreach (MemberInfo member in typeof(T).GetMembers()) { EnumMemberAttribute? attribute = member.GetCustomAttribute(); + if (attribute?.Value == enumMemberValue) { return (T)Enum.Parse(typeof(T), member.Name); } } + throw new JsonException($"Unable to convert \"{enumMemberValue}\" to Enum \"{typeof(T)}\""); } diff --git a/src/dymaptic.GeoBlazor.Core/Extensions/StringExtensions.cs b/src/dymaptic.GeoBlazor.Core/Extensions/StringExtensions.cs index d413f40dd..e0fa509e2 100644 --- a/src/dymaptic.GeoBlazor.Core/Extensions/StringExtensions.cs +++ b/src/dymaptic.GeoBlazor.Core/Extensions/StringExtensions.cs @@ -1,4 +1,4 @@ -namespace dymaptic.GeoBlazor.Core.Extensions; +namespace dymaptic.GeoBlazor.Core.Extensions; internal static class StringExtensions { @@ -15,6 +15,19 @@ public static string ToLowerFirstChar(this string val) }); } + public static string ToUpperFirstChar(this string val) + { + return string.Create(val.Length, val, (span, txt) => + { + span[0] = char.ToUpper(txt[0]); + + for (var i = 1; i < txt.Length; i++) + { + span[i] = txt[i]; + } + }); + } + public static string ToKebabCase(this string val) { bool previousWasDigit = false; diff --git a/src/dymaptic.GeoBlazor.Core/Interfaces/IMeshComponentMaterial.cs b/src/dymaptic.GeoBlazor.Core/Interfaces/IMeshComponentMaterial.cs index a786d9663..c9197c659 100644 --- a/src/dymaptic.GeoBlazor.Core/Interfaces/IMeshComponentMaterial.cs +++ b/src/dymaptic.GeoBlazor.Core/Interfaces/IMeshComponentMaterial.cs @@ -1,7 +1,7 @@ -namespace dymaptic.GeoBlazor.Core.Interfaces; - -[JsonConverter(typeof(MultiTypeConverter))] -public partial interface IMeshComponentMaterial: IMapComponent -{ - // Add custom code to this file to override generated code -} \ No newline at end of file +namespace dymaptic.GeoBlazor.Core.Interfaces; + +[JsonConverter(typeof(MultiTypeConverter))] +public partial interface IMeshComponentMaterial : IMapComponent, IProtobufSerializable +{ + // Add custom code to this file to override generated code +} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Interfaces/IMeshVertexSpace.cs b/src/dymaptic.GeoBlazor.Core/Interfaces/IMeshVertexSpace.cs index 18dce7409..2a7219bf2 100644 --- a/src/dymaptic.GeoBlazor.Core/Interfaces/IMeshVertexSpace.cs +++ b/src/dymaptic.GeoBlazor.Core/Interfaces/IMeshVertexSpace.cs @@ -1,9 +1,9 @@ -namespace dymaptic.GeoBlazor.Core.Interfaces; - -/// -/// Interface for VertexSpace in a Mesh. -/// -[JsonConverter(typeof(MultiTypeConverter))] -public interface IMeshVertexSpace -{ -} \ No newline at end of file +namespace dymaptic.GeoBlazor.Core.Interfaces; + +/// +/// Interface for VertexSpace in a Mesh. +/// +[JsonConverter(typeof(MultiTypeConverter))] +public interface IMeshVertexSpace : IProtobufSerializable +{ +} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Interfaces/IProtobufSerializable.cs b/src/dymaptic.GeoBlazor.Core/Interfaces/IProtobufSerializable.cs index fc299ce40..5284d88cf 100644 --- a/src/dymaptic.GeoBlazor.Core/Interfaces/IProtobufSerializable.cs +++ b/src/dymaptic.GeoBlazor.Core/Interfaces/IProtobufSerializable.cs @@ -1,12 +1,10 @@ -using dymaptic.GeoBlazor.Core.Serialization; - namespace dymaptic.GeoBlazor.Core.Interfaces; /// /// Interface to indicate that a class can be serialized to and from Protobuf format. /// /// The serialization record type. -public interface IProtobufSerializable where T : MapComponentSerializationRecord, new() +public interface IProtobufSerializable : IProtobufSerializable where T : MapComponentSerializationRecord, new() { /// /// Converts the class to its Protobuf serialization record. @@ -16,3 +14,8 @@ namespace dymaptic.GeoBlazor.Core.Interfaces; /// T ToProtobuf(); } + +/// +/// Interface to indicate that a class can be serialized to and from Protobuf format. +/// +public interface IProtobufSerializable; \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/JsModuleManager.cs b/src/dymaptic.GeoBlazor.Core/JsModuleManager.cs index 55718c82c..923e74306 100644 --- a/src/dymaptic.GeoBlazor.Core/JsModuleManager.cs +++ b/src/dymaptic.GeoBlazor.Core/JsModuleManager.cs @@ -8,19 +8,20 @@ public class JsModuleManager /// /// Retrieves the main entry point for the GeoBlazor Core JavaScript module. /// - public async ValueTask GetCoreJsModule(IJSRuntime jsRuntime, IJSObjectReference? proModule, CancellationToken cancellationToken) + public async ValueTask GetCoreJsModule(IJSRuntime jsRuntime, IJSObjectReference? proModule, + CancellationToken cancellationToken) { if (_coreModule is null) { if (proModule is null) { _coreModule = await jsRuntime - .InvokeAsync("import", cancellationToken, + .InvokeAsync("import", cancellationToken, $"./_content/dymaptic.GeoBlazor.Core/js/geoBlazorCore.js?v={_version}"); } else { - _coreModule = await proModule.InvokeAsync("getCore", cancellationToken); + _coreModule = await proModule.InvokeAsync("getCore", cancellationToken); } } @@ -30,7 +31,8 @@ public async ValueTask GetCoreJsModule(IJSRuntime jsRuntime, /// /// Retrieves the main entry point for the optional GeoBlazor Pro JavaScript module. /// - public async ValueTask GetProJsModule(IJSRuntime jsRuntime, CancellationToken cancellationToken) + public async ValueTask GetProJsModule(IJSRuntime jsRuntime, + CancellationToken cancellationToken) { if (_proModule is null && !_proChecked) { @@ -39,23 +41,66 @@ public async ValueTask GetCoreJsModule(IJSRuntime jsRuntime, switch ((int)licenseType) { case >= 100: - + _proModule = await jsRuntime.InvokeAsync("import", cancellationToken, $"./_content/dymaptic.GeoBlazor.Pro/js/geoBlazorPro.js?v={_version}"); + break; default: _proChecked = true; + return null; } } return _proModule; } - - private IJSObjectReference? _proModule; - private IJSObjectReference? _coreModule; - private bool _proChecked; + + /// + /// Retrieves or creates a JavaScript wrapper for a logic component. + /// + /// The JS runtime to use for module loading. + /// The name of the logic component (e.g., "geometryEngine"). + /// The library name (e.g., "Core" or "Pro") + /// A cancellation token. + /// A JavaScript object reference to the component wrapper. + public async ValueTask GetLogicComponent(IJSRuntime jsRuntime, string moduleName, + string library, CancellationToken cancellationToken) + { + if (!_proChecked) + { + await GetProJsModule(jsRuntime, cancellationToken); + } + + if (_coreModule is null) + { + await GetCoreJsModule(jsRuntime, _proModule, cancellationToken); + } + + string moduleFileName = moduleName; + + if (library == "Core" && _proModule is not null) + { + // we need to append ("pro_") on the module names to look up the correct file for Core logic components in a Pro build + moduleFileName = $"pro_{moduleName}"; + } + + string modulePath = $"./_content/dymaptic.GeoBlazor.{(_proModule is null ? "Core" : "Pro")}/js/{moduleFileName + }.js?v={_version}"; + + IJSObjectReference module = + await jsRuntime.InvokeAsync("import", cancellationToken, modulePath); + + // load the default export class from the module + return await _coreModule!.InvokeAsync("getDefaultClassInstanceFromModule", + cancellationToken, module); + } + private readonly string _version = Assembly.GetAssembly(typeof(JsModuleManager))! .GetCustomAttribute()! .InformationalVersion; + + private IJSObjectReference? _proModule; + private IJSObjectReference? _coreModule; + private bool _proChecked; } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Model/GeometryEngine.cs b/src/dymaptic.GeoBlazor.Core/Model/GeometryEngine.cs new file mode 100644 index 000000000..849dfb894 --- /dev/null +++ b/src/dymaptic.GeoBlazor.Core/Model/GeometryEngine.cs @@ -0,0 +1,2301 @@ +// ReSharper disable RedundantEnumerableCastCall +namespace dymaptic.GeoBlazor.Core.Model; + +/// +/// A client-side geometry engine for testing, measuring, and analyzing the spatial relationship between two or more 2D +/// geometries. If more than one geometry is required for any of the methods below, all geometries must have the same +/// spatial reference for the methods to work as expected. +/// +/// ArcGIS +/// Maps SDK for JavaScript +/// +/// +[CodeGenerationIgnore] +public class GeometryEngine( + IAppValidator appValidator, + IJSRuntime jsRuntime, + JsModuleManager jsModuleManager, + AuthenticationManager authenticationManager) + : LogicComponent(appValidator, jsRuntime, jsModuleManager, authenticationManager) +{ + /// + protected override string ComponentName => nameof(GeometryEngine).ToLowerFirstChar(); + + /// + /// Creates planar (or Euclidean) buffer polygons at a specified distance around the input geometries. + /// + /// + /// The GeometryEngine has two methods for buffering geometries client-side: buffer and geodesicBuffer. Use caution + /// when deciding which method to use. As a general rule, use geodesicBuffer if the input geometries have a spatial + /// reference of either WGS84 (wkid: 4326) or Web Mercator. Only use buffer (this method) when attempting to buffer + /// geometries with a projected coordinate system other than Web Mercator. If you need to buffer geometries with a + /// geographic coordinate system other than WGS84 (wkid: 4326), use geometryService.buffer(). + /// + /// + /// The buffer input geometries. + /// + /// + /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the + /// distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one + /// distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of + /// three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The + /// value of the geometry array will be matched one to one with those in the distance array until the final value of + /// the distance array is reached, in which case that value will be applied to the remaining geometries. + /// + /// + /// The resulting buffers. + /// + /// The cancellation token to use for the operation. + public Task Buffer(IEnumerable geometries, IEnumerable distances, + CancellationToken cancellationToken = default) + { + return Buffer(geometries, distances, null, null, cancellationToken); + } + + /// + /// Creates planar (or Euclidean) buffer polygons at a specified distance around the input geometries. + /// + /// + /// The GeometryEngine has two methods for buffering geometries client-side: buffer and geodesicBuffer. Use caution + /// when deciding which method to use. As a general rule, use geodesicBuffer if the input geometries have a spatial + /// reference of either WGS84 (wkid: 4326) or Web Mercator. Only use buffer (this method) when attempting to buffer + /// geometries with a projected coordinate system other than Web Mercator. If you need to buffer geometries with a + /// geographic coordinate system other than WGS84 (wkid: 4326), use geometryService.buffer(). + /// + /// + /// The buffer input geometries. + /// + /// + /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the + /// distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one + /// distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of + /// three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The + /// value of the geometry array will be matched one to one with those in the distance array until the final value of + /// the distance array is reached, in which case that value will be applied to the remaining geometries. + /// + /// + /// Measurement unit of the distance(s). Defaults to the units of the input geometries. + /// + /// + /// The resulting buffers. + /// + /// The cancellation token to use for the operation. + public Task Buffer(IEnumerable geometries, IEnumerable distances, + GeometryEngineLinearUnit? unit, CancellationToken cancellationToken = default) + { + return Buffer(geometries, distances, unit, null, cancellationToken); + } + + /// + /// Creates planar (or Euclidean) buffer polygons at a specified distance around the input geometries. + /// + /// + /// The GeometryEngine has two methods for buffering geometries client-side: buffer and geodesicBuffer. Use caution + /// when deciding which method to use. As a general rule, use geodesicBuffer if the input geometries have a spatial + /// reference of either WGS84 (wkid: 4326) or Web Mercator. Only use buffer (this method) when attempting to buffer + /// geometries with a projected coordinate system other than Web Mercator. If you need to buffer geometries with a + /// geographic coordinate system other than WGS84 (wkid: 4326), use geometryService.buffer(). + /// + /// + /// The buffer input geometries. + /// + /// + /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the + /// distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one + /// distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of + /// three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The + /// value of the geometry array will be matched one to one with those in the distance array until the final value of + /// the distance array is reached, in which case that value will be applied to the remaining geometries. + /// + /// + /// Measurement unit of the distance(s). Defaults to the units of the input geometries. + /// + /// + /// Determines whether the output geometries should be unioned into a single polygon. + /// + /// + /// The resulting buffers. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Buffer(IEnumerable geometries, IEnumerable distances, + GeometryEngineLinearUnit? unit, bool? unionResults, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Buffer), + QueryResultsMaxSizeLimit, cancellationToken, geometries, distances, unit, unionResults); + } + + /// + /// Creates planar (or Euclidean) buffer polygons at a specified distance around the input geometries. + /// + /// + /// The GeometryEngine has two methods for buffering geometries client-side: buffer and geodesicBuffer. Use caution + /// when deciding which method to use. As a general rule, use geodesicBuffer if the input geometries have a spatial + /// reference of either WGS84 (wkid: 4326) or Web Mercator. Only use buffer (this method) when attempting to buffer + /// geometries with a projected coordinate system other than Web Mercator. If you need to buffer geometries with a + /// geographic coordinate system other than WGS84 (wkid: 4326), use geometryService.buffer(). + /// + /// + /// The buffer input geometries. + /// + /// + /// The specified distance(s) for buffering. + /// + /// + /// The resulting buffer. + /// + /// The cancellation token to use for the operation. + public Task Buffer(Geometry geometry, double distance, CancellationToken cancellationToken = default) + { + return Buffer(geometry, distance, null, cancellationToken); + } + + /// + /// Creates planar (or Euclidean) buffer polygons at a specified distance around the input geometries. + /// + /// + /// The GeometryEngine has two methods for buffering geometries client-side: buffer and geodesicBuffer. Use caution + /// when deciding which method to use. As a general rule, use geodesicBuffer if the input geometries have a spatial + /// reference of either WGS84 (wkid: 4326) or Web Mercator. Only use buffer (this method) when attempting to buffer + /// geometries with a projected coordinate system other than Web Mercator. If you need to buffer geometries with a + /// geographic coordinate system other than WGS84 (wkid: 4326), use geometryService.buffer(). + /// + /// + /// The buffer input geometries. + /// + /// + /// The specified distance(s) for buffering. + /// + /// + /// Measurement unit of the distance(s). Defaults to the units of the input geometries. + /// + /// + /// The resulting buffer. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Buffer(Geometry geometry, double distance, GeometryEngineLinearUnit? unit, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Buffer), + QueryResultsMaxSizeLimit, cancellationToken, geometry, distance, unit); + } + + /// + /// Calculates the clipped geometry from a target geometry by an envelope. + /// + /// + /// The geometry to be clipped. + /// + /// + /// The envelope used to clip. + /// + /// + /// Clipped geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Clip(Geometry geometry, Extent extent, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Clip), + QueryResultsMaxSizeLimit, cancellationToken, geometry, extent); + } + + /// + /// Indicates if one geometry contains another geometry. + /// + /// + /// The geometry that is tested for the "contains" relationship to the other geometry. Think of this geometry as the + /// potential "container" of the insideGeometry. + /// + /// + /// The geometry that is tested for the "within" relationship to the containerGeometry. + /// + /// + /// Returns true if the containerGeometry contains the insideGeometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Contains(Geometry containerGeometry, Geometry insideGeometry, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Contains), + QueryResultsMaxSizeLimit, cancellationToken, containerGeometry, insideGeometry); + } + + /// + /// Calculates the convex hull of one or more geometries. A convex hull is the smallest convex polygon that encloses a + /// group of geometries or vertices. The input can be a single geometry (such as a polyline) or an array of any + /// geometry type. The hull is typically a polygon but can also be a polyline or a point in degenerate cases. + /// + /// + /// The input geometries used to calculate the convex hull. The input array can include various geometry types. + /// + /// + /// Indicates whether to merge the output into a single geometry (usually a polygon). + /// + /// + /// Returns the convex hull of the input geometries. This is usually a polygon, but can also be a polyline (if the + /// input is a set of points or polylines forming a straight line), or a point (in degenerate cases). + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task ConvexHull(IEnumerable geometries, bool? merge = null, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(ConvexHull), + QueryResultsMaxSizeLimit, cancellationToken, geometries, merge); + } + + /// + /// Calculates the convex hull of one or more geometries. A convex hull is the smallest convex polygon that encloses a + /// group of geometries or vertices. The input can be a single geometry (such as a polyline) or an array of any + /// geometry type. The hull is typically a polygon but can also be a polyline or a point in degenerate cases. + /// + /// + /// The input geometry used to calculate the convex hull. + /// + /// + /// Returns the convex hull of the input geometries. This is usually a polygon, but can also be a polyline (if the + /// input is a set of points or polylines forming a straight line), or a point (in degenerate cases). + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task ConvexHull(Geometry geometry, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(ConvexHull), + QueryResultsMaxSizeLimit, cancellationToken, geometry); + } + + /// + /// Indicates if one geometry crosses another geometry. + /// + /// + /// The geometry to cross. + /// + /// + /// The geometry being crossed. + /// + /// + /// Returns true if geometry1 crosses geometry2. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Crosses(Geometry geometry1, Geometry geometry2, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Crosses), + QueryResultsMaxSizeLimit, cancellationToken, geometry1, geometry2); + } + + /// + /// Splits the input Polyline or Polygon where it crosses a cutting Polyline. For Polylines, all left cuts are grouped + /// together in the first Geometry. Right cuts and coincident cuts are grouped in the second Geometry and each + /// undefined cut, along with any uncut parts, are output as separate Polylines. For Polygons, all left cuts are + /// grouped in the first Polygon, all right cuts are grouped in the second Polygon, and each undefined cut, along with + /// any leftover parts after cutting, are output as a separate Polygon. If no cuts are returned then the array will be + /// empty. An undefined cut will only be produced if a left cut or right cut was produced and there was a part left + /// over after cutting, or a cut is bounded to the left and right of the cutter. + /// + /// + /// The geometry to be cut. + /// + /// + /// The polyline to cut the geometry. + /// + /// + /// Returns an array of geometries created by cutting the input geometry with the cutter. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Cut(Geometry geometry, Polyline cutter, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Cut), + QueryResultsMaxSizeLimit, cancellationToken, geometry, cutter); + } + + /// + /// Densify geometries by plotting points between existing vertices. + /// + /// + /// The geometry to be densified. + /// + /// + /// The maximum segment length allowed. Must be a positive value. + /// + /// + /// Measurement unit for maxSegmentLength. Defaults to the units of the input geometry. + /// + /// + /// The densified geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Densify(Geometry geometry, double maxSegmentLength, + GeometryEngineLinearUnit? maxSegmentLengthUnit = null, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Densify), + QueryResultsMaxSizeLimit, cancellationToken, geometry, maxSegmentLength, maxSegmentLengthUnit); + } + + /// + /// Creates the difference of two geometries. The resultant geometry is the portion of inputGeometry not in the + /// subtractor. The dimension of the subtractor has to be equal to or greater than that of the inputGeometry. + /// + /// + /// The input geometries to subtract from. + /// + /// + /// The geometry being subtracted from inputGeometry. + /// + /// + /// Returns the geometry of inputGeometry minus the subtractor geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Difference(IEnumerable geometries, Geometry subtractor, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Difference), + QueryResultsMaxSizeLimit, cancellationToken, geometries, subtractor); + } + + /// + /// Creates the difference of two geometries. The resultant geometry is the portion of inputGeometry not in the + /// subtractor. The dimension of the subtractor has to be equal to or greater than that of the inputGeometry. + /// + /// + /// The input geometry to subtract from. + /// + /// + /// The geometry being subtracted from inputGeometry. + /// + /// + /// Returns the geometry of inputGeometry minus the subtractor geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Difference(Geometry geometry, Geometry subtractor, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Difference), + QueryResultsMaxSizeLimit, cancellationToken, geometry, subtractor); + } + + /// + /// Indicates if one geometry is disjoint (doesn't intersect in any way) with another geometry. + /// + /// + /// The base geometry that is tested for the "disjoint" relationship to the other geometry. + /// + /// + /// The comparison geometry that is tested for the "disjoint" relationship to the other geometry. + /// + /// + /// Returns true if geometry1 and geometry2 are disjoint (don't intersect in any way). + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Disjoint(Geometry geometry1, Geometry geometry2, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Disjoint), + QueryResultsMaxSizeLimit, cancellationToken, geometry1, geometry2); + } + + /// + /// Calculates the shortest planar distance between two geometries. Distance is reported in the linear units specified + /// by distanceUnit or, if distanceUnit is null, the units of the spatialReference of input geometry. + /// + /// + /// First input geometry. + /// + /// + /// Second input geometry. + /// + /// + /// Measurement unit of the return value. Defaults to the units of the input geometries. + /// + /// + /// Distance between the two input geometries. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Distance(Geometry geometry1, Geometry geometry2, + GeometryEngineLinearUnit? distanceUnit = null, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Distance), + QueryResultsMaxSizeLimit, cancellationToken, geometry1, geometry2, distanceUnit); + } + + /// + /// Indicates if two geometries are equal. + /// + /// + /// In ArcGIS for JS, this method is called `Equals`. However, this term has special meaning in .NET, so we have + /// renamed here. + /// + /// + /// First input geometry. + /// + /// + /// Second input geometry. + /// + /// + /// Returns true if the two input geometries are equal. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task AreEqual(Geometry geometry1, Geometry geometry2, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(AreEqual), + QueryResultsMaxSizeLimit, cancellationToken, geometry1, geometry2); + } + + /// + /// Returns an object containing additional information about the input spatial reference. + /// + /// + /// The input spatial reference. + /// + /// + /// Resolves to a object. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + [Obsolete] + public async Task ExtendedSpatialReferenceInfo(SpatialReference spatialReference, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), + nameof(ExtendedSpatialReferenceInfo), QueryResultsMaxSizeLimit, cancellationToken, spatialReference); + } + + /// + /// Flips a geometry on the horizontal axis. Can optionally be flipped around a point. + /// + /// + /// The input geometry to be flipped. + /// + /// + /// Point to flip the geometry around. Defaults to the centroid of the geometry. + /// + /// + /// The flipped geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task FlipHorizontal(Geometry geometry, Point? flipOrigin = null, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(FlipHorizontal), + QueryResultsMaxSizeLimit, cancellationToken, geometry, flipOrigin); + } + + /// + /// Flips a geometry on the vertical axis. Can optionally be flipped around a point. + /// + /// + /// The input geometry to be flipped. + /// + /// + /// Point to flip the geometry around. Defaults to the centroid of the geometry. + /// + /// + /// The flipped geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task FlipVertical(Geometry geometry, Point? flipOrigin = null, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(FlipVertical), + QueryResultsMaxSizeLimit, cancellationToken, geometry, flipOrigin); + } + + /// + /// Performs the generalize operation on the geometries in the cursor. Point and Multipoint geometries are left + /// unchanged. Envelope is converted to a Polygon and then generalized. + /// + /// + /// The input geometry to be generalized. + /// + /// + /// The maximum allowed deviation from the generalized geometry to the original geometry. + /// + /// + /// When true the degenerate parts of the geometry will be removed from the output (may be undesired for drawing). + /// + /// + /// Measurement unit for maxDeviation. Defaults to the units of the input geometry. + /// + /// + /// The generalized geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Generalize(Geometry geometry, double maxDeviation, bool? removeDegenerateParts = null, + GeometryEngineLinearUnit? maxDeviationUnit = null, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Generalize), + QueryResultsMaxSizeLimit, cancellationToken, geometry, maxDeviation, removeDegenerateParts, + maxDeviationUnit); + } + + /// + /// Calculates the area of the input geometry. As opposed to planarArea(), geodesicArea takes into account the + /// curvature of the earth when performing this calculation. Therefore, when using input geometries with a spatial + /// reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to calculate areas using + /// geodesicArea(). If the input geometries have a projected coordinate system other than Web Mercator, use + /// planarArea() instead. + /// + /// + /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. + /// + /// + /// The input polygon + /// + /// + /// Measurement unit of the return value. Defaults to the units of the input geometries. + /// + /// + /// Area of the input geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task GeodesicArea(Polygon geometry, GeometryEngineAreaUnit? unit = null, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(GeodesicArea), + QueryResultsMaxSizeLimit, cancellationToken, geometry, unit); + } + + /// + /// Creates geodesic buffer polygons at a specified distance around the input geometries. When calculating distances, + /// this method takes the curvature of the earth into account, which provides highly accurate results when dealing with + /// very large geometries and/or geometries that spatially vary on a global scale where one projected coordinate system + /// could not accurately plot coordinates and measure distances for all the geometries. + /// + /// + /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. In general, if your input + /// geometries are assigned one of those two spatial references, you should always use geodesicBuffer() to obtain the + /// most accurate results for those geometries. If needing to buffer points assigned a projected coordinate system + /// other than Web Mercator, use buffer() instead. If the input geometries have a geographic coordinate system other + /// than WGS84 (wkid: 4326), use geometryService.buffer(). + /// + /// + /// The buffer input geometries + /// + /// + /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the + /// distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one + /// distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of + /// three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The + /// value of the geometry array will be matched one to one with those in the distance array until the final value of + /// the distance array is reached, in which case that value will be applied to the remaining geometries. + /// + /// + /// The resulting buffers + /// + /// The cancellation token to use for the operation. + public Task GeodesicBuffer(IEnumerable geometries, IEnumerable distances, + CancellationToken cancellationToken = default) + { + return GeodesicBuffer(geometries, distances, null, null, cancellationToken); + } + + /// + /// Creates geodesic buffer polygons at a specified distance around the input geometries. When calculating distances, + /// this method takes the curvature of the earth into account, which provides highly accurate results when dealing with + /// very large geometries and/or geometries that spatially vary on a global scale where one projected coordinate system + /// could not accurately plot coordinates and measure distances for all the geometries. + /// + /// + /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. In general, if your input + /// geometries are assigned one of those two spatial references, you should always use geodesicBuffer() to obtain the + /// most accurate results for those geometries. If needing to buffer points assigned a projected coordinate system + /// other than Web Mercator, use buffer() instead. If the input geometries have a geographic coordinate system other + /// than WGS84 (wkid: 4326), use geometryService.buffer(). + /// + /// + /// The buffer input geometries + /// + /// + /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the + /// distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one + /// distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of + /// three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The + /// value of the geometry array will be matched one to one with those in the distance array until the final value of + /// the distance array is reached, in which case that value will be applied to the remaining geometries. + /// + /// + /// Measurement unit of the distance(s). Defaults to the units of the input geometries. + /// + /// + /// The resulting buffers + /// + /// The cancellation token to use for the operation. + public Task GeodesicBuffer(IEnumerable geometries, IEnumerable distances, + GeometryEngineLinearUnit? unit, CancellationToken cancellationToken = default) + { + return GeodesicBuffer(geometries, distances, unit, null, cancellationToken); + } + + /// + /// Creates geodesic buffer polygons at a specified distance around the input geometries. When calculating distances, + /// this method takes the curvature of the earth into account, which provides highly accurate results when dealing with + /// very large geometries and/or geometries that spatially vary on a global scale where one projected coordinate system + /// could not accurately plot coordinates and measure distances for all the geometries. + /// + /// + /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. In general, if your input + /// geometries are assigned one of those two spatial references, you should always use geodesicBuffer() to obtain the + /// most accurate results for those geometries. If needing to buffer points assigned a projected coordinate system + /// other than Web Mercator, use buffer() instead. If the input geometries have a geographic coordinate system other + /// than WGS84 (wkid: 4326), use geometryService.buffer(). + /// + /// + /// The buffer input geometries + /// + /// + /// The specified distance(s) for buffering. The length of the geometry array does not have to equal the length of the + /// distance array. For example, if you pass an array of four geometries: [g1, g2, g3, g4] and an array with one + /// distance: [d1], all four geometries will be buffered by the single distance value. If instead you use an array of + /// three distances: [d1, d2, d3], g1 will be buffered by d1, g2 by d2, and g3 and g4 will both be buffered by d3. The + /// value of the geometry array will be matched one to one with those in the distance array until the final value of + /// the distance array is reached, in which case that value will be applied to the remaining geometries. + /// + /// + /// Measurement unit of the distance(s). Defaults to the units of the input geometries. + /// + /// + /// Determines whether the output geometries should be unioned into a single polygon. + /// + /// + /// The resulting buffers + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task GeodesicBuffer(IEnumerable geometries, IEnumerable distances, + GeometryEngineLinearUnit? unit, bool? unionResults, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(GeodesicBuffer), + QueryResultsMaxSizeLimit, cancellationToken, geometries, distances, unit, unionResults); + } + + /// + /// Creates geodesic buffer polygons at a specified distance around the input geometries. When calculating distances, + /// this method takes the curvature of the earth into account, which provides highly accurate results when dealing with + /// very large geometries and/or geometries that spatially vary on a global scale where one projected coordinate system + /// could not accurately plot coordinates and measure distances for all the geometries. + /// + /// + /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. In general, if your input + /// geometries are assigned one of those two spatial references, you should always use geodesicBuffer() to obtain the + /// most accurate results for those geometries. If needing to buffer points assigned a projected coordinate system + /// other than Web Mercator, use buffer() instead. If the input geometries have a geographic coordinate system other + /// than WGS84 (wkid: 4326), use geometryService.buffer(). + /// + /// + /// The buffer input geometry + /// + /// + /// The specified distance for buffering. + /// + /// + /// The resulting buffers + /// + /// The cancellation token to use for the operation. + public Task GeodesicBuffer(Geometry geometry, double distance, + CancellationToken cancellationToken = default) + { + if (geometry.Type == GeometryType.Multipoint) + { + // in Pro there is an extension method called GeodesicBufferMultipoint + // we need to call it via reflection + MethodInfo? multiPointMethodInfo = Assembly.Load("dymaptic.GeoBlazor.Pro") + ? + .GetType("dymaptic.GeoBlazor.Pro.Model.GeometryEngineProExtensions") + ? + .GetMethod("GeodesicBufferMultipoint"); + + if (multiPointMethodInfo is not null) + { + return (Task)multiPointMethodInfo.Invoke(this, + [this, geometry, distance, cancellationToken])!; + } + } + + return GeodesicBuffer(geometry, distance, null, cancellationToken); + } + + /// + /// Creates geodesic buffer polygons at a specified distance around the input geometries. When calculating distances, + /// this method takes the curvature of the earth into account, which provides highly accurate results when dealing with + /// very large geometries and/or geometries that spatially vary on a global scale where one projected coordinate system + /// could not accurately plot coordinates and measure distances for all the geometries. + /// + /// + /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. In general, if your input + /// geometries are assigned one of those two spatial references, you should always use geodesicBuffer() to obtain the + /// most accurate results for those geometries. If needing to buffer points assigned a projected coordinate system + /// other than Web Mercator, use buffer() instead. If the input geometries have a geographic coordinate system other + /// than WGS84 (wkid: 4326), use geometryService.buffer(). + /// + /// + /// The buffer input geometry + /// + /// + /// The specified distance for buffering. + /// + /// + /// Measurement unit of the distance. Defaults to the units of the input geometry. + /// + /// + /// The resulting buffers + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task GeodesicBuffer(Geometry geometry, double distance, GeometryEngineLinearUnit? unit, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(GeodesicBuffer), + QueryResultsMaxSizeLimit, cancellationToken, geometry, distance, unit); + } + + /// + /// Returns a geodesically densified version of the input geometry. Use this function to draw the line(s) of the + /// geometry along great circles. + /// + /// + /// A polyline or polygon to densify. + /// + /// + /// The maximum segment length allowed (in meters if a maxSegmentLengthUnit is not provided). This must be a positive + /// value. + /// + /// + /// Returns the densified geometry. + /// + /// The cancellation token to use for the operation. + public Task GeodesicDensify(Geometry geometry, double maxSegmentLength, + CancellationToken cancellationToken = default) + { + return GeodesicDensify(geometry, maxSegmentLength, null, cancellationToken); + } + + /// + /// Returns a geodesically densified version of the input geometry. Use this function to draw the line(s) of the + /// geometry along great circles. + /// + /// + /// A polyline or polygon to densify. + /// + /// + /// The maximum segment length allowed (in meters if a maxSegmentLengthUnit is not provided). This must be a positive + /// value. + /// + /// + /// Measurement unit for maxSegmentLength. If not provided, the unit will default to meters. + /// + /// + /// Returns the densified geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task GeodesicDensify(Geometry geometry, double maxSegmentLength, + GeometryEngineLinearUnit? maxSegmentLengthUnit, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(GeodesicDensify), + QueryResultsMaxSizeLimit, cancellationToken, geometry, maxSegmentLength, maxSegmentLengthUnit); + } + + /// + /// Calculates the length of the input geometry. As opposed to planarLength(), geodesicLength() takes into account the + /// curvature of the earth when performing this calculation. Therefore, when using input geometries with a spatial + /// reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to calculate lengths using + /// geodesicLength(). If the input geometries have a projected coordinate system other than Web Mercator, use + /// planarLength() instead. + /// + /// + /// This method only works with WGS84 (wkid: 4326) and Web Mercator spatial references. + /// + /// + /// The input geometry. + /// + /// + /// Measurement unit of the return value. Defaults to the units of the input geometry. + /// + /// + /// Length of the input geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task GeodesicLength(Geometry geometry, GeometryEngineLinearUnit? unit = null, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(GeodesicLength), + QueryResultsMaxSizeLimit, cancellationToken, geometry, unit); + } + + /// + /// Creates new geometries from the intersections between two geometries. If the input geometries have different + /// dimensions (i.e. point = 0; polyline = 1; polygon = 2), then the result's dimension will be equal to the lowest + /// dimension of the inputs. The table below describes the expected output for various combinations of geometry types. + /// + /// + /// The input array of geometries. + /// + /// + /// The geometry to intersect with geometries1. + /// + /// + /// The intersections of the geometries. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Intersect(IEnumerable geometries1, Geometry geometry2, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Intersect), + QueryResultsMaxSizeLimit, cancellationToken, geometries1, geometry2); + } + + /// + /// Creates new geometries from the intersections between two geometries. If the input geometries have different + /// dimensions (i.e. point = 0; polyline = 1; polygon = 2), then the result's dimension will be equal to the lowest + /// dimension of the inputs. + /// + /// + /// The input geometry. + /// + /// + /// The geometry to intersect with geometry1. + /// + /// + /// The intersections of the geometries. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Intersect(Geometry geometry1, Geometry geometry2, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Intersect), + QueryResultsMaxSizeLimit, cancellationToken, geometry1, geometry2); + } + + /// + /// Indicates if one geometry intersects another geometry. + /// + /// + /// The geometry that is tested for the intersects relationship to the other geometry. + /// + /// + /// The geometry being intersected. + /// + /// + /// Returns true if the input geometries intersect each other. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Intersects(Geometry geometry1, Geometry geometry2, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Intersects), + QueryResultsMaxSizeLimit, cancellationToken, geometry1, geometry2); + } + + /// + /// Indicates if the given geometry is topologically simple. In a simplified geometry, no polygon rings or polyline + /// paths will overlap, and no self-intersection will occur. + /// + /// + /// The input geometry. + /// + /// + /// Returns true if the geometry is topologically simple. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task IsSimple(Geometry geometry, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(IsSimple), + QueryResultsMaxSizeLimit, cancellationToken, geometry); + } + + /// + /// Finds the coordinate of the geometry that is closest to the specified point. + /// + /// + /// The geometry to consider. + /// + /// + /// The point used to search the nearest coordinate in the geometry. + /// + /// + /// Returns an object containing the nearest coordinate. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task NearestCoordinate(Geometry geometry, Point inputPoint, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(NearestCoordinate), + QueryResultsMaxSizeLimit, cancellationToken, geometry, inputPoint); + } + + /// + /// Finds the vertex on the geometry nearest to the specified point. + /// + /// + /// The geometry to consider. + /// + /// + /// The point used to search the nearest vertex in the geometry. + /// + /// + /// Returns an object containing the nearest vertex. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task NearestVertex(Geometry geometry, Point inputPoint, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(NearestVertex), + QueryResultsMaxSizeLimit, cancellationToken, geometry, inputPoint); + } + + /// + /// Finds all vertices in the given distance from the specified point, sorted from the closest to the furthest and + /// returns them as an array of Objects. + /// + /// + /// The geometry to consider. + /// + /// + /// The point from which to measure. + /// + /// + /// The distance to search from the inputPoint in the units of the view's spatial reference. + /// + /// + /// The maximum number of vertices to return. + /// + /// + /// An array of objects containing the nearest vertices within the given searchRadius. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task NearestVertices(Geometry geometry, Point inputPoint, double searchRadius, + int maxVertexCountToReturn, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(NearestVertices), + QueryResultsMaxSizeLimit, cancellationToken, geometry, inputPoint, searchRadius, maxVertexCountToReturn); + } + + /// + /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is + /// similar to buffering, but produces a one-sided result. Point and multipoint geometries are not supported. + /// + /// + /// The geometries to offset. + /// + /// + /// The distance to offset the input geometry. Unless the unit option is set, the default is the geometry's spatial + /// reference unit. + /// + /// + /// The offset geometries. + /// + /// The cancellation token to use for the operation. + public Task Offset(IEnumerable geometries, double offsetDistance, + CancellationToken cancellationToken = default) + { + return Offset(geometries, offsetDistance, null, null, null, null, + cancellationToken); + } + + /// + /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is + /// similar to buffering, but produces a one-sided result. Point and multipoint geometries are not supported. + /// + /// + /// The geometries to offset. + /// + /// + /// The distance to offset the input geometry. Unless the unit option is set, the default is the geometry's spatial + /// reference unit. + /// + /// + /// The length unit of the offset distance. The default is the input geometries spatial reference unit. An error will + /// be thrown if this is set for Geographic Coordinate Systems. + /// + /// + /// The offset geometries. + /// + /// The cancellation token to use for the operation. + public Task Offset(IEnumerable geometries, double offsetDistance, + GeometryEngineLinearUnit? offsetUnit, CancellationToken cancellationToken = default) + { + return Offset(geometries, offsetDistance, offsetUnit, null, null, null, + cancellationToken); + } + + /// + /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is + /// similar to buffering, but produces a one-sided result. Point and multipoint geometries are not supported. + /// + /// + /// The geometries to offset. + /// + /// + /// The distance to offset the input geometry. Unless the unit option is set, the default is the geometry's spatial + /// reference unit. + /// + /// + /// The length unit of the offset distance. The default is the input geometries spatial reference unit. An error will + /// be thrown if this is set for Geographic Coordinate Systems. + /// + /// + /// The . Defines the join type of the offset geometry. Inner corners are always mitered. + /// Default value: "round" - Round - a circular arc that is tangent to the ends of both offset line segments. - Miter - + /// the offset line segments are extended to their intersection point forming a sharp angle, unless that extension + /// exceeds the miterLimit, in which case the result is a bevel. - Bevel - the offset line segments are not extended; + /// their endpoints are joined by a straight line. - Square - same as miter for minor arcs greater than 90 degrees. For + /// all other minor arcs, the offset line segments are extended by an extra distance before their endpoints are joined. + /// + /// + /// The offset geometries. + /// + /// The cancellation token to use for the operation. + public Task Offset(IEnumerable geometries, double offsetDistance, + GeometryEngineLinearUnit? offsetUnit, JoinType? joinType, CancellationToken cancellationToken = default) + { + return Offset(geometries, offsetDistance, offsetUnit, joinType, null, null, + cancellationToken); + } + + /// + /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is + /// similar to buffering, but produces a one-sided result. Point and multipoint geometries are not supported. + /// + /// + /// The geometries to offset. + /// + /// + /// The distance to offset the input geometry. Unless the unit option is set, the default is the geometry's spatial + /// reference unit. + /// + /// + /// The length unit of the offset distance. The default is the input geometries spatial reference unit. An error will + /// be thrown if this is set for Geographic Coordinate Systems. + /// + /// + /// The . Defines the join type of the offset geometry. Inner corners are always mitered. + /// Default value: "round" - Round - a circular arc that is tangent to the ends of both offset line segments. - Miter - + /// the offset line segments are extended to their intersection point forming a sharp angle, unless that extension + /// exceeds the miterLimit, in which case the result is a bevel. - Bevel - the offset line segments are not extended; + /// their endpoints are joined by a straight line. - Square - same as miter for minor arcs greater than 90 degrees. For + /// all other minor arcs, the offset line segments are extended by an extra distance before their endpoints are joined. + /// + /// + /// Applicable only to mitered joins. Controls the appearance of angled lines to avoid excessively long and pointy + /// corners. The miterLimit is multiplied by the offset distance and the result is the maximum mitered offset: joins + /// which would result in a larger mitered offset will be beveled instead. + /// + /// + /// The offset geometries. + /// + /// The cancellation token to use for the operation. + public Task Offset(IEnumerable geometries, double offsetDistance, + GeometryEngineLinearUnit? offsetUnit, JoinType? joinType, double? miterLimit, + CancellationToken cancellationToken = default) + { + return Offset(geometries, offsetDistance, offsetUnit, joinType, miterLimit, null, + cancellationToken); + } + + /// + /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is + /// similar to buffering, but produces a one-sided result. Point and multipoint geometries are not supported. + /// + /// + /// The geometries to offset. + /// + /// + /// The distance to offset the input geometry. Unless the unit option is set, the default is the geometry's spatial + /// reference unit. + /// + /// + /// The length unit of the offset distance. The default is the input geometries spatial reference unit. An error will + /// be thrown if this is set for Geographic Coordinate Systems. + /// + /// + /// The . Defines the join type of the offset geometry. Inner corners are always mitered. + /// Default value: "round" - Round - a circular arc that is tangent to the ends of both offset line segments. - Miter - + /// the offset line segments are extended to their intersection point forming a sharp angle, unless that extension + /// exceeds the miterLimit, in which case the result is a bevel. - Bevel - the offset line segments are not extended; + /// their endpoints are joined by a straight line. - Square - same as miter for minor arcs greater than 90 degrees. For + /// all other minor arcs, the offset line segments are extended by an extra distance before their endpoints are joined. + /// + /// + /// Applicable only to mitered joins. Controls the appearance of angled lines to avoid excessively long and pointy + /// corners. The miterLimit is multiplied by the offset distance and the result is the maximum mitered offset: joins + /// which would result in a larger mitered offset will be beveled instead. + /// + /// + /// The maximum distance of the resulting segments compared to the true circular arc (used only when joins is round). + /// The algorithm never produces more than around 180 vertices for each round join. + /// + /// + /// The offset geometries. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Offset(IEnumerable geometries, double offsetDistance, + GeometryEngineLinearUnit? offsetUnit, JoinType? joinType, double? miterLimit, + double? flattenError, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Offset), + QueryResultsMaxSizeLimit, cancellationToken, geometries, offsetDistance, offsetUnit, joinType, miterLimit, + flattenError); + } + + /// + /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is + /// similar to buffering, but produces a one-sided result. Point and multipoint geometries are not supported. + /// + /// + /// The geometry to offset. + /// + /// + /// The distance to offset the input geometry. Unless the unit option is set, the default is the geometry's spatial + /// reference unit. + /// + /// + /// The offset geometry. + /// + /// The cancellation token to use for the operation. + public Task Offset(Geometry geometry, double offsetDistance, + CancellationToken cancellationToken = default) + { + return Offset(geometry, offsetDistance, null, null, null, null, + cancellationToken); + } + + /// + /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is + /// similar to buffering, but produces a one-sided result. Point and multipoint geometries are not supported. + /// + /// + /// The geometry to offset. + /// + /// + /// The distance to offset the input geometry. Unless the unit option is set, the default is the geometry's spatial + /// reference unit. + /// + /// + /// The length unit of the offset distance. The default is the input geometries spatial reference unit. An error will + /// be thrown if this is set for Geographic Coordinate Systems. + /// + /// + /// The offset geometry. + /// + /// The cancellation token to use for the operation. + public Task Offset(Geometry geometry, double offsetDistance, + GeometryEngineLinearUnit? offsetUnit, CancellationToken cancellationToken = default) + { + return Offset(geometry, offsetDistance, offsetUnit, null, null, null, + cancellationToken); + } + + /// + /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is + /// similar to buffering, but produces a one-sided result. Point and multipoint geometries are not supported. + /// + /// + /// The geometry to offset. + /// + /// + /// The distance to offset the input geometry. Unless the unit option is set, the default is the geometry's spatial + /// reference unit. + /// + /// + /// The length unit of the offset distance. The default is the input geometries spatial reference unit. An error will + /// be thrown if this is set for Geographic Coordinate Systems. + /// + /// + /// The . Defines the join type of the offset geometry. Inner corners are always mitered. + /// Default value: "round" - Round - a circular arc that is tangent to the ends of both offset line segments. - Miter - + /// the offset line segments are extended to their intersection point forming a sharp angle, unless that extension + /// exceeds the miterLimit, in which case the result is a bevel. - Bevel - the offset line segments are not extended; + /// their endpoints are joined by a straight line. - Square - same as miter for minor arcs greater than 90 degrees. For + /// all other minor arcs, the offset line segments are extended by an extra distance before their endpoints are joined. + /// + /// + /// The offset geometry. + /// + /// The cancellation token to use for the operation. + public Task Offset(Geometry geometry, double offsetDistance, + GeometryEngineLinearUnit? offsetUnit, JoinType? joinType, CancellationToken cancellationToken = default) + { + return Offset(geometry, offsetDistance, offsetUnit, joinType, null, null, + cancellationToken); + } + + /// + /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is + /// similar to buffering, but produces a one-sided result. Point and multipoint geometries are not supported. + /// + /// + /// The geometry to offset. + /// + /// + /// The distance to offset the input geometry. Unless the unit option is set, the default is the geometry's spatial + /// reference unit. + /// + /// + /// The length unit of the offset distance. The default is the input geometries spatial reference unit. An error will + /// be thrown if this is set for Geographic Coordinate Systems. + /// + /// + /// The . Defines the join type of the offset geometry. Inner corners are always mitered. + /// Default value: "round" - Round - a circular arc that is tangent to the ends of both offset line segments. - Miter - + /// the offset line segments are extended to their intersection point forming a sharp angle, unless that extension + /// exceeds the miterLimit, in which case the result is a bevel. - Bevel - the offset line segments are not extended; + /// their endpoints are joined by a straight line. - Square - same as miter for minor arcs greater than 90 degrees. For + /// all other minor arcs, the offset line segments are extended by an extra distance before their endpoints are joined. + /// + /// + /// Applicable only to mitered joins. Controls the appearance of angled lines to avoid excessively long and pointy + /// corners. The miterLimit is multiplied by the offset distance and the result is the maximum mitered offset: joins + /// which would result in a larger mitered offset will be beveled instead. + /// + /// + /// The offset geometry. + /// + /// The cancellation token to use for the operation. + public Task Offset(Geometry geometry, double offsetDistance, + GeometryEngineLinearUnit? offsetUnit, JoinType? joinType, double? miterLimit, + CancellationToken cancellationToken = default) + { + return Offset(geometry, offsetDistance, offsetUnit, joinType, miterLimit, null, + cancellationToken); + } + + /// + /// The offset operation creates a geometry that is a constant planar distance from an input polyline or polygon. It is + /// similar to buffering, but produces a one-sided result. Point and multipoint geometries are not supported. + /// + /// + /// The geometry to offset. + /// + /// + /// The distance to offset the input geometry. Unless the unit option is set, the default is the geometry's spatial + /// reference unit. + /// + /// + /// The length unit of the offset distance. The default is the input geometries spatial reference unit. An error will + /// be thrown if this is set for Geographic Coordinate Systems. + /// + /// + /// The . Defines the join type of the offset geometry. Inner corners are always mitered. + /// Default value: "round" - Round - a circular arc that is tangent to the ends of both offset line segments. - Miter - + /// the offset line segments are extended to their intersection point forming a sharp angle, unless that extension + /// exceeds the miterLimit, in which case the result is a bevel. - Bevel - the offset line segments are not extended; + /// their endpoints are joined by a straight line. - Square - same as miter for minor arcs greater than 90 degrees. For + /// all other minor arcs, the offset line segments are extended by an extra distance before their endpoints are joined. + /// + /// + /// Applicable only to mitered joins. Controls the appearance of angled lines to avoid excessively long and pointy + /// corners. The miterLimit is multiplied by the offset distance and the result is the maximum mitered offset: joins + /// which would result in a larger mitered offset will be beveled instead. + /// + /// + /// The maximum distance of the resulting segments compared to the true circular arc (used only when joins is round). + /// The algorithm never produces more than around 180 vertices for each round join. + /// + /// + /// The offset geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Offset(Geometry geometry, double offsetDistance, + GeometryEngineLinearUnit? offsetUnit, JoinType? joinType, double? miterLimit, + double? flattenError, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Offset), + QueryResultsMaxSizeLimit, cancellationToken, geometry, offsetDistance, offsetUnit, joinType, miterLimit, + flattenError); + } + + /// + /// Indicates if one geometry overlaps another geometry. + /// + /// + /// The base geometry that is tested for the "overlaps" relationship with the other geometry. + /// + /// + /// The comparison geometry that is tested for the "overlaps" relationship with the other geometry. + /// + /// + /// Returns true if the two geometries overlap. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Overlaps(Geometry geometry1, Geometry geometry2, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Overlaps), + QueryResultsMaxSizeLimit, cancellationToken, geometry1, geometry2); + } + + /// + /// Calculates the area of the input geometry. As opposed to geodesicArea(), planarArea() performs this calculation + /// using projected coordinates and does not take into account the earth's curvature. When using input geometries with + /// a spatial reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to calculate areas using + /// geodesicArea(). If the input geometries have a projected coordinate system other than Web Mercator, use + /// planarArea() instead. + /// + /// + /// The input polygon. + /// + /// The cancellation token to use for the operation. + public Task PlanarArea(Polygon geometry, CancellationToken cancellationToken = default) + { + return PlanarArea(geometry, null, cancellationToken); + } + + /// + /// Calculates the area of the input geometry. As opposed to geodesicArea(), planarArea() performs this calculation + /// using projected coordinates and does not take into account the earth's curvature. When using input geometries with + /// a spatial reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to calculate areas using + /// geodesicArea(). If the input geometries have a projected coordinate system other than Web Mercator, use + /// planarArea() instead. + /// + /// + /// The input polygon. + /// + /// + /// Measurement unit of the return value. Defaults to the units of the input geometries. + /// + /// + /// The area of the input geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task PlanarArea(Polygon geometry, GeometryEngineAreaUnit? unit, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(PlanarArea), + QueryResultsMaxSizeLimit, cancellationToken, geometry, unit); + } + + /// + /// Calculates the length of the input geometry. As opposed to geodesicLength(), planarLength() uses projected + /// coordinates and does not take into account the curvature of the earth when performing this calculation. When using + /// input geometries with a spatial reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to + /// calculate lengths using geodesicLength(). If the input geometries have a projected coordinate system other than Web + /// Mercator, use planarLength() instead. + /// + /// + /// The input geometry. + /// + /// + /// The length of the input geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task PlanarLength(Geometry geometry, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(PlanarLength), + QueryResultsMaxSizeLimit, cancellationToken, geometry); + } + + /// + /// Calculates the length of the input geometry. As opposed to geodesicLength(), planarLength() uses projected + /// coordinates and does not take into account the curvature of the earth when performing this calculation. When using + /// input geometries with a spatial reference of either WGS84 (wkid: 4326) or Web Mercator, it is best practice to + /// calculate lengths using geodesicLength(). If the input geometries have a projected coordinate system other than Web + /// Mercator, use planarLength() instead. + /// + /// + /// The input geometry. + /// + /// + /// Measurement unit of the return value. Defaults to the units of the input geometries. + /// + /// + /// The length of the input geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task PlanarLength(Geometry geometry, GeometryEngineLinearUnit? unit, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(PlanarLength), + QueryResultsMaxSizeLimit, cancellationToken, geometry, unit); + } + + /// + /// Indicates if the given DE-9IM relation is true for the two geometries. + /// + /// + /// The first geometry for the relation. + /// + /// + /// The second geometry for the relation. + /// + /// + /// The Dimensionally Extended 9 Intersection Model (DE-9IM) matrix relation (encoded as a string) to test against the + /// relationship of the two geometries. This string contains the test result of each intersection represented in the + /// DE-9IM matrix. Each result is one character of the string and may be represented as either a number (maximum + /// dimension returned: 0,1,2), a Boolean value (T or F), or a mask character (for ignoring results: '*'). For example, + /// each of the following DE-9IM string codes are valid for testing whether a polygon geometry completely contains a + /// line geometry: TTTFFTFFT (Boolean), 'T******FF*' (ignore irrelevant intersections), or '102FF*FF*' (dimension + /// form). Each returns the same result. See + /// this article and + /// + /// this + /// ArcGIS help page + /// + /// for more information about the DE-9IM model and how string codes are constructed. + /// + /// + /// Returns true if the relation of the input geometries is accurate. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Relate(Geometry geometry1, Geometry geometry2, string relation, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Relate), + QueryResultsMaxSizeLimit, cancellationToken, geometry1, geometry2, relation); + } + + /// + /// Rotates a geometry counterclockwise by the specified number of degrees. Rotation is around the given rotation + /// point, or the centroid if not specified. + /// + /// + /// The geometry to rotate. + /// + /// + /// The rotation angle in degrees. + /// + /// + /// Point to rotate the geometry around. Defaults to the centroid of the geometry. + /// + /// + /// The rotated geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Rotate(Geometry geometry, double angle, Point? rotationOrigin, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Rotate), + QueryResultsMaxSizeLimit, cancellationToken, geometry, angle, rotationOrigin); + } + + /// + /// Performs the simplify operation on the geometry, which alters the given geometries to make their definitions + /// topologically legal with respect to their geometry type. At the end of a simplify operation, no polygon rings or + /// polyline paths will overlap, and no self-intersection will occur. + /// + /// + /// The geometry to be simplified. + /// + /// + /// The simplified geometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Simplify(Geometry geometry, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Simplify), + QueryResultsMaxSizeLimit, cancellationToken, geometry); + } + + /// + /// Creates the symmetric difference of two geometries. The symmetric difference includes the parts that are in either + /// of the sets, but not in both. + /// + /// + /// One of the Geometry instances in the XOR operation. + /// + /// + /// One of the Geometry instances in the XOR operation. + /// + /// + /// The symmetric differences of the two geometries. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task SymmetricDifference(IEnumerable leftGeometries, Geometry rightGeometry, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(SymmetricDifference), + QueryResultsMaxSizeLimit, cancellationToken, leftGeometries, rightGeometry); + } + + /// + /// Creates the symmetric difference of two geometries. The symmetric difference includes the parts that are in either + /// of the sets, but not in both. + /// + /// + /// One of the Geometry instances in the XOR operation. + /// + /// + /// One of the Geometry instances in the XOR operation. + /// + /// + /// The symmetric differences of the two geometries. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task SymmetricDifference(Geometry leftGeometry, Geometry rightGeometry, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(SymmetricDifference), + QueryResultsMaxSizeLimit, cancellationToken, leftGeometry, rightGeometry); + } + + /// + /// Indicates if one geometry touches another geometry. + /// + /// + /// The geometry to test the "touches" relationship with the other geometry. + /// + /// + /// The geometry to be touched. + /// + /// + /// When true, geometry1 touches geometry2. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Touches(Geometry geometry1, Geometry geometry2, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Touches), + QueryResultsMaxSizeLimit, cancellationToken, geometry1, geometry2); + } + + /// + /// All inputs must be of the same type of geometries and share one spatial reference. + /// + /// + /// An array of Geometries to union. + /// + /// + /// The union of the geometries + /// + public Task Union(params Geometry[] geometries) + { + return Union(geometries, CancellationToken.None); + } + + /// + /// All inputs must be of the same type of geometries and share one spatial reference. + /// + /// + /// An array of Geometries to union. + /// + /// + /// The union of the geometries + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Union(IEnumerable geometries, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Union), + QueryResultsMaxSizeLimit, cancellationToken, geometries.Cast()); + } + + /// + /// Indicates if one geometry is within another geometry. + /// + /// + /// The base geometry that is tested for the "within" relationship to the other geometry. + /// + /// + /// The comparison geometry that is tested for the "contains" relationship to the other geometry. + /// + /// + /// Returns true if innerGeometry is within outerGeometry. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Within(Geometry innerGeometry, Geometry outerGeometry, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Within), + QueryResultsMaxSizeLimit, cancellationToken, innerGeometry, outerGeometry); + } + + /// + /// Creates a new instance of this class and initializes it with values from a JSON object generated from an ArcGIS + /// product. The object passed into the input json parameter often comes from a response to a query operation in the + /// REST API or a toJSON() method from another ArcGIS product. See the + /// + /// Using + /// fromJSON() + /// + /// topic in the Guide for details and examples of when and how to use this function. + /// + /// + /// A JSON representation of the instance in the ArcGIS format. See the + /// + /// ArcGIS + /// REST API documentation + /// + /// for examples of the structure of various input JSON objects. + /// + /// + /// Returns a new geometry instance. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task FromArcGisJson(string json, CancellationToken cancellationToken = default) + where T : Geometry + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(FromArcGisJson), + QueryResultsMaxSizeLimit, cancellationToken, json, typeof(T).Name); + } + + /// + /// Converts an instance of this class to its ArcGIS portal JSON representation. See the + /// + /// Using + /// fromJSON() + /// + /// guide topic for more information. + /// + /// + /// The geometry to convert. + /// + /// + /// The + /// + /// ArcGIS + /// portal JSON + /// + /// representation of an instance of this class. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task ToArcGisJson(T geometry, CancellationToken cancellationToken = default) + where T : Geometry + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(ToArcGisJson), + QueryResultsMaxSizeLimit, cancellationToken, geometry); + } + + /// + /// Creates a deep clone of the geometry. + /// + /// + /// Unlike the Clone methods in the Geometry classes, this method does a loop through the ArcGIS JS SDK. Therefore, if + /// you are having issues with unpopulated fields in the geometry, try using this method instead. + /// + /// The geometry to clone + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Clone(T geometry, CancellationToken cancellationToken = default) + where T : Geometry + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Clone), + QueryResultsMaxSizeLimit, cancellationToken, geometry); + } + + /// + /// Centers the given extent to the given point, and returns a new extent. + /// + /// + /// The input extent. + /// + /// + /// The point to center the extent on. + /// + /// + /// The centered extent. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task CenterExtentAt(Extent extent, Point point, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(CenterExtentAt), + QueryResultsMaxSizeLimit, cancellationToken, extent, point); + } + + /// + /// Expands the extent by the given factor. For example, a value of 1.5 will expand the extent to be 50 percent larger + /// than the original extent. + /// + /// + /// The input extent. + /// + /// + /// The factor by which to expand the extent. + /// + /// + /// The expanded extent. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task Expand(Extent extent, double factor, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(Expand), + QueryResultsMaxSizeLimit, cancellationToken, extent, factor); + } + + /// + /// Returns an array with either one Extent that's been shifted to within +/- 180 or two Extents if the original extent + /// intersects the International Dateline. + /// + /// + /// The input extent. + /// + /// + /// An array with either one Extent that's been shifted to within +/- 180 or two Extents if the original extent + /// intersects the International Dateline. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task NormalizeExtent(Extent extent, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(NormalizeExtent), + QueryResultsMaxSizeLimit, cancellationToken, extent); + } + + /// + /// Modifies the extent geometry in-place with X and Y offsets in map units. + /// + /// + /// The input extent. + /// + /// + /// The offset distance in map units for the X-coordinate. + /// + /// + /// The offset distance in map units for the Y-coordinate. + /// + /// + /// The offset distance in map units for the Z-coordinate. + /// + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task OffsetExtent(Extent extent, double dx, double dy, double dz = 0, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(OffsetExtent), + QueryResultsMaxSizeLimit, cancellationToken, extent, dx, dy, dz); + } + + /// + /// Modifies the point geometry in-place by shifting the X-coordinate to within +/- 180 span in map units. + /// + /// + /// The input point. + /// + /// + /// Returns a point with a normalized x-value. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task NormalizePoint(Point point, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(NormalizePoint), + QueryResultsMaxSizeLimit, cancellationToken, point); + } + + /// + /// Adds a path, or line segment, to the polyline. When added, the index of the path is incremented by one. + /// + /// + /// The polyline to add the path to. Will return a new modified copy. + /// + /// + /// The polyline path to add as a . + /// + /// + /// Returns a new polyline with the added path. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task AddPath(Polyline polyline, MapPath points, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(AddPath), + QueryResultsMaxSizeLimit, cancellationToken, polyline, points); + } + + /// + /// Adds a path, or line segment, to the polyline. When added, the index of the path is incremented by one. + /// + /// + /// The polyline to add the path to. Will return a new modified copy. + /// + /// + /// The polyline path to add as an array of s. + /// + /// + /// Returns a new polyline with the added path. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task AddPath(Polyline polyline, Point[] points, + CancellationToken cancellationToken = default) + { + var mapPoints = new List(); + + foreach (var p in points) + { + mapPoints.Add(new MapPoint(p.X ?? p.Longitude!.Value, p.Y ?? p.Latitude!.Value)); + } + + return await AddPath(polyline, new MapPath(mapPoints), cancellationToken); + } + + /// + /// Returns a point specified by a path and point in the path. + /// + /// + /// The polyline to get the point from. + /// + /// + /// The index of the path in the polyline. + /// + /// + /// The index of the point in the path. + /// + /// + /// Returns the point along the Polyline located in the given path and point indices. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task GetPoint(Polyline polyline, int pathIndex, int pointIndex, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(GeometryEngine), nameof(GetPoint), + QueryResultsMaxSizeLimit, cancellationToken, polyline, pathIndex, pointIndex); + } + + /// + /// Inserts a new point into a polyline and returns the modified line. + /// + /// + /// The polyline to insert the point into. + /// + /// + /// The index of the path in which to insert a point. + /// + /// + /// The index of the inserted point in the path. + /// + /// + /// The point to insert into the polyline. + /// + /// + /// Returns a new polyline with the inserted point. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task InsertPoint(Polyline polyline, int pathIndex, int pointIndex, Point point, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(InsertPoint), + QueryResultsMaxSizeLimit, cancellationToken, polyline, pathIndex, pointIndex, point); + } + + /// + /// Removes a path from the Polyline. The index specifies which path to remove. + /// + /// + /// The polyline to remove the path from. + /// + /// + /// The index of the path to remove. + /// + /// The cancellation token to use for the operation. + /// + /// Returns an object with the modified polyline and the removed path. + /// + [SerializedMethod] + public async Task<(Polyline PolyLine, Point[] Path)> RemovePath(Polyline polyline, int index, + CancellationToken cancellationToken = default) + { + var result = + await InvokeAsync(nameof(ProjectionEngine), nameof(RemovePath), + QueryResultsMaxSizeLimit, cancellationToken, polyline, index); + + if (result.Length < 3) // need at least two points for the path + { + throw new InvalidOperationException($"No path found at index {index} on polyline"); + } + + return ((Polyline)result[0], result.Skip(1).Cast().ToArray()); + } + + /// + /// Removes a point from the polyline at the given pointIndex within the path identified by the given pathIndex. + /// + /// + /// The polyline to remove the point from. + /// + /// + /// The index of the path in which to remove a point. + /// + /// + /// The index of the point in the path to remove. + /// + /// The cancellation token to use for the operation. + /// + /// Returns an object with the modified polyline and the removed point. + /// + [SerializedMethod] + public async Task<(Polyline Polyline, Point Point)> RemovePoint(Polyline polyline, int pathIndex, int pointIndex, + CancellationToken cancellationToken = default) + { + var result = + await InvokeAsync(nameof(ProjectionEngine), nameof(RemovePoint), + QueryResultsMaxSizeLimit, cancellationToken, polyline, pathIndex, pointIndex); + + if (result.Length < 2) + { + throw new InvalidOperationException($"No point found at path index {pathIndex} and point index {pointIndex + } on polyline"); + } + + return ((Polyline)result[0], (Point)result[1]); + } + + /// + /// Updates a point in a polyline and returns the modified polyline. + /// + /// + /// The polyline to update the point in. + /// + /// + /// The index of the path in which to update a point. + /// + /// + /// The index of the point in the path to update. + /// + /// + /// The new point to update the polyline with. + /// + /// + /// Returns a new polyline with the updated point. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task SetPoint(Polyline polyline, int pathIndex, int pointIndex, Point point, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(GeometryEngine), nameof(SetPoint), + QueryResultsMaxSizeLimit, cancellationToken, polyline, pathIndex, pointIndex, point); + } + + /// + /// Adds a ring to the Polygon. The ring can be one of the following: an array of numbers or an array of points. When + /// added the index of the ring is incremented by one. + /// + /// + /// The polygon to add the ring to. + /// + /// + /// A polygon ring. The first and last coordinates/points in the ring must be the same. + /// + /// + /// Returns a new polygon with the added ring. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task AddRing(Polygon polygon, MapPath points, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(GeometryEngine), nameof(AddRing), + QueryResultsMaxSizeLimit, cancellationToken, polygon, points); + } + + /// + /// Adds a ring to the Polygon. The ring can be one of the following: an array of numbers or an array of points. When + /// added the index of the ring is incremented by one. + /// + /// + /// The polygon to add the ring to. + /// + /// + /// A polygon ring. The first and last coordinates/points in the ring must be the same. + /// + /// + /// Returns a new polygon with the added ring. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task AddRing(Polygon polygon, Point[] points, CancellationToken cancellationToken = default) + { + var mapPoints = new List(); + + foreach (var p in points) + { + mapPoints.Add(new MapPoint(p.X ?? p.Longitude!.Value, p.Y ?? p.Latitude!.Value)); + } + + return await AddRing(polygon, new MapPath(mapPoints), cancellationToken); + } + + /// + /// Converts the given Extent to a Polygon instance. This is useful for scenarios in which you would like to display an + /// area of interest, which is typically defined by an Extent or bounding box, as a polygon with a fill symbol in the + /// view. Some geoprocessing tools require input geometries to be of a Polygon type and not an Extent. + /// + /// + /// An extent object to convert to a polygon. + /// + /// + /// A polygon instance representing the given extent. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task PolygonFromExtent(Extent extent, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(GeometryEngine), nameof(PolygonFromExtent), + QueryResultsMaxSizeLimit, cancellationToken, extent); + } + + /// + /// Returns a point specified by a ring and point in the ring. + /// + /// + /// The polygon to get the point from. + /// + /// + /// The index of the ring containing the desired point. + /// + /// + /// The index of the desired point within the ring. + /// + /// + /// Returns the point at the specified ring index and point index. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task GetPoint(Polygon polygon, int ringIndex, int pointIndex, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(GeometryEngine), nameof(GetPoint), + QueryResultsMaxSizeLimit, cancellationToken, polygon, ringIndex, pointIndex); + } + + /// + /// Inserts a new point into the polygon. + /// + /// + /// The polygon to insert the point into. + /// + /// + /// The index of the ring in which to insert the point. + /// + /// + /// The index of the point to insert within the ring. + /// + /// + /// The point to insert into the polygon. + /// + /// + /// Returns a new polygon with the inserted point. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task InsertPoint(Polygon polygon, int ringIndex, int pointIndex, Point point, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(GeometryEngine), nameof(InsertPoint), + QueryResultsMaxSizeLimit, cancellationToken, polygon, ringIndex, pointIndex, point); + } + + /// + /// Checks if a Polygon ring is clockwise + /// + /// + /// The polygon to check the ring on. + /// + /// + /// A polygon ring defined as a . The first and last coordinates/points in the ring must be the + /// same. + /// + /// + /// Returns true if the ring is clockwise and false for counterclockwise. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task IsClockwise(Polygon polygon, MapPath ring, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(GeometryEngine), nameof(IsClockwise), + QueryResultsMaxSizeLimit, cancellationToken, polygon, ring); + } + + /// + /// Checks if a Polygon ring is clockwise + /// + /// + /// The polygon to check the ring on. + /// + /// + /// A polygon ring defined as an array of s. The first and last coordinates/points in the ring must + /// be the same. + /// + /// + /// Returns true if the ring is clockwise and false for counterclockwise. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task IsClockwise(Polygon polygon, Point[] ring, CancellationToken cancellationToken = default) + { + var mapPoints = new List(); + + foreach (var p in ring) + { + if ((p.X is null && p.Longitude is null) || (p.Y is null && p.Latitude is null)) + { + throw new ArgumentException("Points must have X and Y coordinates"); + } + + mapPoints.Add(new MapPoint(p.X ?? p.Longitude!.Value, p.Y ?? p.Latitude!.Value)); + } + + return await IsClockwise(polygon, new MapPath(mapPoints), cancellationToken); + } + + /// + /// Removes a point from the polygon at the given pointIndex within the ring identified by the given ringIndex. + /// + /// + /// The polyline to remove the point from. + /// + /// + /// The index of the ring containing the point to remove. + /// + /// + /// The index of the point to remove within the ring. + /// + /// The cancellation token to use for the operation. + /// + /// Returns an object with the modified polygon and the removed point. + /// + [SerializedMethod] + public async Task<(Polygon Polygon, Point Point)> RemovePoint(Polygon polygon, int ringIndex, int pointIndex, + CancellationToken cancellationToken = default) + { + var result = + await InvokeAsync(nameof(GeometryEngine), nameof(RemovePoint), + QueryResultsMaxSizeLimit, cancellationToken, polygon, ringIndex, pointIndex); + + if (result.Length < 2) + { + throw new InvalidOperationException($"No point found at ring index {ringIndex} and point index {pointIndex + } on polygon"); + } + + return ((Polygon)result[0], (Point)result[1]); + } + + /// + /// Removes a ring from the Polygon. The index specifies which ring to remove. + /// + /// + /// The polygon to remove the ring from. + /// + /// + /// The index of the ring to remove. + /// + /// The cancellation token to use for the operation. + /// + /// Returns an object with the modified polygon and the removed ring. + /// + [SerializedMethod] + public async Task<(Polygon Polygon, Point[] Ring)> RemoveRing(Polygon polygon, int index, + CancellationToken cancellationToken = default) + { + var result = + await InvokeAsync(nameof(GeometryEngine), nameof(RemoveRing), + QueryResultsMaxSizeLimit, cancellationToken, polygon, index); + + if (result.Length < 3) // need at least two points for the ring + { + throw new InvalidOperationException($"No ring found at index {index} on polygon"); + } + + return ((Polygon)result[0], result.Skip(1).Cast().ToArray()); + } + + /// + /// Updates a point in a polygon and returns the modified polygon. + /// + /// + /// The polygon to update the point in. + /// + /// + /// The index of the ring containing the point to update. + /// + /// + /// The index of the point to update within the ring. + /// + /// + /// The new point to update the polygon with. + /// + /// + /// Returns a new polygon with the updated point. + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task SetPoint(Polygon polygon, int ringIndex, int pointIndex, Point point, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(GeometryEngine), nameof(SetPoint), + QueryResultsMaxSizeLimit, cancellationToken, polygon, ringIndex, pointIndex, point); + } + + /// + /// Retrieves the center point of the extent in map units. + /// + /// The extent to measure + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task GetExtentCenter(Extent extent, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(GeometryEngine), nameof(GetExtentCenter), + QueryResultsMaxSizeLimit, cancellationToken, extent); + } + + /// + /// Retrieves the height of the extent in map units (the distance between ymin and ymax). + /// + /// The extent to measure + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task GetExtentHeight(Extent extent, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(GetExtentHeight), + QueryResultsMaxSizeLimit, cancellationToken, extent); + } + + /// + /// Retrieves the width of the extent in map units (the distance between xmin and xmax). + /// + /// The extent to measure + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task GetExtentWidth(Extent extent, CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(ProjectionEngine), nameof(GetExtentWidth), + QueryResultsMaxSizeLimit, cancellationToken, extent); + } +} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Model/LocationService.cs b/src/dymaptic.GeoBlazor.Core/Model/LocationService.cs new file mode 100644 index 000000000..0e854fea0 --- /dev/null +++ b/src/dymaptic.GeoBlazor.Core/Model/LocationService.cs @@ -0,0 +1,546 @@ +// ReSharper disable MethodOverloadWithOptionalParameter +namespace dymaptic.GeoBlazor.Core.Model; + +/// +/// GeoBlazor Docs +/// A convenience module for importing locator functions when developing with +/// TypeScript. +/// ArcGIS Maps SDK for JavaScript +/// +[CodeGenerationIgnore] +public class LocationService( + IAppValidator appValidator, + IJSRuntime jsRuntime, + JsModuleManager jsModuleManager, + AuthenticationManager authenticationManager) + : LogicComponent(appValidator, jsRuntime, jsModuleManager, authenticationManager) +{ + /// + protected override string ComponentName => nameof(LocationService); + + private const string ESRIGeoLocationUrl = "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer"; + + +#region AddressesToLocationsWithAddress + + /// + /// Find address candidates for multiple input addresses. + /// Uses the default ESRI geolocation service. + /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results + /// + /// The input addresses in the format supported by the geocode service. + /// + /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only + /// applies to the World Geocode Service. See the World Geocoding Service documentation for more information. + /// + /// + /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". + /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. + /// + /// + /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. + /// + /// + /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial + /// reference of the input geometries when performing a reverse geocode and in the default spatial reference returned + /// by the service if finding locations by address. + /// + /// + /// Additional options to be used for the data request + /// + /// + /// The cancellation token to use for the asynchronous operation. + /// + public async Task> AddressesToLocations(List
addresses, string? countryCode = null, + List? categories = null, LocationType? locationType = null, + SpatialReference? outSpatialReference = null, RequestOptions? requestOptions = null, + CancellationToken cancellationToken = default) + { + return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode, categories, locationType, + outSpatialReference, requestOptions, cancellationToken); + } + + /// + /// Find address candidates for multiple input addresses. + /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results + /// + /// URL to the ArcGIS Server REST resource that represents a locator service. + /// The input addresses in the format supported by the geocode service. + /// + /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only + /// applies to the World Geocode Service. See the World Geocoding Service documentation for more information. + /// + /// + /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". + /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. + /// + /// + /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. + /// + /// + /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial + /// reference of the input geometries when performing a reverse geocode and in the default spatial reference returned + /// by the service if finding locations by address. + /// + /// + /// Additional options to be used for the data request + /// + /// + /// The cancellation token to use for the asynchronous operation. + /// + [SerializedMethod] + public async Task> AddressesToLocations(string url, List
addresses, + string? countryCode = null, List? categories = null, LocationType? locationType = null, + SpatialReference? outSpatialReference = null, RequestOptions? requestOptions = null, + CancellationToken cancellationToken = default) + { + return await InvokeAsync>(nameof(LocationService), + nameof(AddressesToLocations), QueryResultsMaxSizeLimit, cancellationToken, url, + addresses, countryCode, categories, locationType, + outSpatialReference, requestOptions); + } + +#endregion + + +#region AddressesToLocationsWithString + + /// + /// Find address candidates for multiple input addresses. + /// Uses the default ESRI geolocation service. + /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results + /// + /// The input addresses in the format supported by the geocode service. + /// + /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only + /// applies to the World Geocode Service. See the World Geocoding Service documentation for more information. + /// + /// + /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". + /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. + /// + /// + /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. + /// + /// + /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial + /// reference of the input geometries when performing a reverse geocode and in the default spatial reference returned + /// by the service if finding locations by address. + /// + /// + /// Additional options to be used for the data request + /// + /// + /// The name of the single line address field for the ArcGIS Locator Service (for ArcGIS 10+), defaults to 'address'. + /// + /// + /// The cancellation token to use for the asynchronous operation. + /// + public async Task> AddressesToLocations(List addresses, string? countryCode = null, + List? categories = null, LocationType? locationType = null, + SpatialReference? outSpatialReference = null, RequestOptions? requestOptions = null, + string? addressSearchStringParameterName = null, CancellationToken cancellationToken = default) + { + return await AddressesToLocations(ESRIGeoLocationUrl, addresses, countryCode, categories, locationType, + outSpatialReference, requestOptions, addressSearchStringParameterName, cancellationToken); + } + + /// + /// Find address candidates for multiple input addresses. + /// Note: If using as API token: the token must have "Geocode (Stored)" enabled to get results + /// + /// URL to the ArcGIS Server REST resource that represents a locator service. + /// The input addresses in the format supported by the geocode service. + /// + /// Limits the results to only search in the country provided. For example US for United States or SE for Sweden. Only + /// applies to the World Geocode Service. See the World Geocoding Service documentation for more information. + /// + /// + /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". + /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. + /// + /// + /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. + /// + /// + /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial + /// reference of the input geometries when performing a reverse geocode and in the default spatial reference returned + /// by the service if finding locations by address. + /// + /// + /// Additional options to be used for the data request + /// + /// + /// The name of the single line address field for the ArcGIS Locator Service (for ArcGIS 10+), defaults to 'address'. + /// + /// + /// The cancellation token to use for the asynchronous operation. + /// + [SerializedMethod] + public async Task> AddressesToLocations(string url, List addresses, + string? countryCode = null, List? categories = null, LocationType? locationType = null, + SpatialReference? outSpatialReference = null, RequestOptions? requestOptions = null, + string? addressSearchStringParameterName = null, CancellationToken cancellationToken = default) + { + return await InvokeAsync>(nameof(LocationService), + nameof(AddressesToLocations), QueryResultsMaxSizeLimit, cancellationToken, url, + addresses, countryCode, categories, locationType, + outSpatialReference, requestOptions, addressSearchStringParameterName); + } + +#endregion + + +#region AddressToLocationsWithAddress + + /// + /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the + /// address parameter. + /// Uses the default ESRI geolocation service. + /// + /// the various address fields accepted by the corresponding geocode service. + /// + /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". + /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. + /// + /// + /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. + /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. + /// + /// Allows the results of single geocode transactions to be persisted. + /// Used to weight returned results for a specified area. + /// + /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. + /// + /// A suggestLocations result ID (magicKey). Used to query for a specific results information. + /// Maximum results to return from the query. + /// + /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you + /// specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify + /// the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection + /// candidate fields. + /// + /// + /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial + /// reference of the input geometries when performing a reverse geocode and in the default spatial reference returned + /// by the service if finding locations by address. + /// + /// + /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. + /// + /// + /// Additional options to be used for the data request + /// + /// + /// The cancellation token to use for the asynchronous operation. + /// + public async Task> AddressToLocations(Address address, List? categories = null, + string? countryCode = null, bool? forStorage = null, Point? location = null, LocationType? locationType = null, + string? magicKey = null, int? maxLocations = null, List? outFields = null, + SpatialReference? outSpatialReference = null, Extent? searchExtent = null, + RequestOptions? requestOptions = null, CancellationToken cancellationToken = default) + { + return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, + locationType, magicKey, maxLocations, outFields, outSpatialReference, searchExtent, requestOptions, + cancellationToken); + } + + /// + /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the + /// address parameter. + /// + /// URL to the ArcGIS Server REST resource that represents a locator service. + /// the various address fields accepted by the corresponding geocode service. + /// + /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". + /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. + /// + /// + /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. + /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. + /// + /// Allows the results of single geocode transactions to be persisted. + /// Used to weight returned results for a specified area. + /// + /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. + /// + /// A suggestLocations result ID (magicKey). Used to query for a specific results information. + /// Maximum results to return from the query. + /// + /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you + /// specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify + /// the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection + /// candidate fields. + /// + /// + /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial + /// reference of the input geometries when performing a reverse geocode and in the default spatial reference returned + /// by the service if finding locations by address. + /// + /// + /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. + /// + /// + /// Additional options to be used for the data request + /// + /// + /// The cancellation token to use for the asynchronous operation. + /// + [SerializedMethod] + public async Task> AddressToLocations(string url, Address address, + List? categories = null, string? countryCode = null, bool? forStorage = null, Point? location = null, + LocationType? locationType = null, string? magicKey = null, int? maxLocations = null, + List? outFields = null, SpatialReference? outSpatialReference = null, Extent? searchExtent = null, + RequestOptions? requestOptions = null, CancellationToken cancellationToken = default) + { + return await InvokeAsync>(nameof(LocationService), + nameof(AddressToLocations), QueryResultsMaxSizeLimit, cancellationToken, url, address, + categories, countryCode, forStorage, location, locationType, magicKey, + maxLocations, outFields, outSpatialReference, searchExtent, requestOptions); + } + +#endregion + + +#region AddressToLocationsWithString + + /// + /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the + /// address parameter. + /// Uses the default ESRI geolocation service. + /// + /// the various address fields accepted by the corresponding geocode service. + /// + /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". + /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. + /// + /// + /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. + /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. + /// + /// Allows the results of single geocode transactions to be persisted. + /// Used to weight returned results for a specified area. + /// + /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. + /// + /// A suggestLocations result ID (magicKey). Used to query for a specific results information. + /// Maximum results to return from the query. + /// + /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you + /// specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify + /// the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection + /// candidate fields. + /// + /// + /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial + /// reference of the input geometries when performing a reverse geocode and in the default spatial reference returned + /// by the service if finding locations by address. + /// + /// + /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. + /// + /// + /// Additional options to be used for the data request + /// + /// + /// The name of the single line address field for the ArcGIS Locator Service (for ArcGIS 10+), defaults to 'address'. + /// + /// + /// The cancellation token to use for the asynchronous operation. + /// + public async Task> AddressToLocations(string address, List? categories = null, + string? countryCode = null, bool? forStorage = null, Point? location = null, LocationType? locationType = null, + string? magicKey = null, int? maxLocations = null, List? outFields = null, + SpatialReference? outSpatialReference = null, Extent? searchExtent = null, + RequestOptions? requestOptions = null, string? addressSearchStringParameterName = null, + CancellationToken cancellationToken = default) + { + return await AddressToLocations(ESRIGeoLocationUrl, address, categories, countryCode, forStorage, location, + locationType, magicKey, maxLocations, outFields, outSpatialReference, searchExtent, requestOptions, + addressSearchStringParameterName, cancellationToken); + } + + /// + /// Sends a request to the ArcGIS REST geocode resource to find candidates for a single address specified in the + /// address parameter. + /// + /// URL to the ArcGIS Server REST resource that represents a locator service. + /// the various address fields accepted by the corresponding geocode service. + /// + /// Limit result to one or more categories. For example, "Populated Place" or "Scandinavian Food". + /// Only applies to the World Geocode Service. See Category filtering (World Geocoding Service) for more information. + /// + /// + /// Limit result to a specific country. For example, "US" for United States or "SE" for Sweden. + /// Only applies to the World Geocode Service. See Geocode coverage (World Geocoding Service) for more information. + /// + /// Allows the results of single geocode transactions to be persisted. + /// Used to weight returned results for a specified area. + /// + /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. + /// + /// A suggestLocations result ID (magicKey). Used to query for a specific results information. + /// Maximum results to return from the query. + /// + /// The list of fields included in the returned result set. This list is a comma delimited list of field names. If you + /// specify the shape field in the list of return fields, it is ignored. For non-intersection addresses you can specify + /// the candidate fields as defined in the geocode service. For intersection addresses you can specify the intersection + /// candidate fields. + /// + /// + /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial + /// reference of the input geometries when performing a reverse geocode and in the default spatial reference returned + /// by the service if finding locations by address. + /// + /// + /// Defines the extent within which the geocode server will search. Requires ArcGIS Server version 10.1 or greater. + /// + /// + /// Additional options to be used for the data request + /// + /// + /// The name of the single line address field for the ArcGIS Locator Service (for ArcGIS 10+), defaults to 'address'. + /// + /// + /// The cancellation token to use for the asynchronous operation. + /// + [SerializedMethod] + public async Task> AddressToLocations(string url, string address, + List? categories = null, string? countryCode = null, bool? forStorage = null, Point? location = null, + LocationType? locationType = null, string? magicKey = null, int? maxLocations = null, + List? outFields = null, SpatialReference? outSpatialReference = null, Extent? searchExtent = null, + RequestOptions? requestOptions = null, string? addressSearchStringParameterName = null, + CancellationToken cancellationToken = default) + { + return await InvokeAsync>(nameof(LocationService), + nameof(AddressToLocations), QueryResultsMaxSizeLimit, cancellationToken, url, address, + categories, countryCode, forStorage, location, locationType, magicKey, + maxLocations, outFields, outSpatialReference, searchExtent, requestOptions, + addressSearchStringParameterName); + } + +#endregion + + +#region LocationToAddress + + /// + /// Locates an address based on a given point. + /// Uses the default ESRI geolocation service. + /// + /// + /// The point at which to search for the closest address. The location should be in the same spatial reference as that + /// of the geocode service. + /// + /// + /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. + /// + /// + /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial + /// reference of the input geometries when performing a reverse geocode and in the default spatial reference returned + /// by the service if finding locations by address. + /// + /// + /// Additional options to be used for the data request + /// + /// The cancellation token to use for the operation. + public Task LocationToAddress(Point location, LocationType? locationType = null, + SpatialReference? outSpatialReference = null, RequestOptions? requestOptions = null, + CancellationToken cancellationToken = default) + { + return LocationToAddress(ESRIGeoLocationUrl, location, locationType, + outSpatialReference, requestOptions, cancellationToken); + } + + /// + /// Locates an address based on a given point. + /// + /// URL to the ArcGIS Server REST resource that represents a locator service. + /// + /// The point at which to search for the closest address. The location should be in the same spatial reference as that + /// of the geocode service. + /// + /// + /// Define the type of location, either "street" or "rooftop", of the point returned from the World Geocoding Service. + /// + /// + /// The spatial reference of the output geometries. If not specified, the output geometries are in the spatial + /// reference of the input geometries when performing a reverse geocode and in the default spatial reference returned + /// by the service if finding locations by address. + /// + /// + /// Additional options to be used for the data request + /// + /// The cancellation token to use for the operation. + [SerializedMethod] + public async Task LocationToAddress(string url, Point location, LocationType? locationType = null, + SpatialReference? outSpatialReference = null, RequestOptions? requestOptions = null, + CancellationToken cancellationToken = default) + { + return await InvokeAsync(nameof(LocationService), nameof(LocationToAddress), + QueryResultsMaxSizeLimit, cancellationToken, url, location, locationType, outSpatialReference, + requestOptions); + } + +#endregion + + +#region SuggestLocations + + /// + /// Get character by character auto complete suggestions. + /// Uses the default ESRI geolocation service. + /// + /// + /// Defines a normalized location point that is used to sort geocoding candidates based upon their proximity to the + /// given location. + /// + /// + /// The input text entered by a user which is used by the suggest operation to generate a list of possible matches. + /// + /// + /// A place or address type which can be used to filter suggest results. The parameter supports input of single + /// category values or multiple comma-separated values. + /// + /// + /// Additional options to be used for the data request + /// + /// The cancellation token. + public Task> SuggestLocations(Point location, string text, + List? categories = null, RequestOptions? requestOptions = null, + CancellationToken cancellationToken = default) + { + return SuggestLocations(ESRIGeoLocationUrl, location, text, categories, requestOptions, cancellationToken); + } + + /// + /// Get character by character auto complete suggestions. + /// + /// URL to the ArcGIS Server REST resource that represents a locator service. + /// + /// Defines a normalized location point that is used to sort geocoding candidates based upon their proximity to the + /// given location. + /// + /// + /// The input text entered by a user which is used by the suggest operation to generate a list of possible matches. + /// + /// + /// A place or address type which can be used to filter suggest results. The parameter supports input of single + /// category values or multiple comma-separated values. + /// + /// + /// Additional options to be used for the data request + /// + /// The cancellation token. + [SerializedMethod] + public async Task> SuggestLocations(string url, Point location, string text, + List? categories = null, RequestOptions? requestOptions = null, + CancellationToken cancellationToken = default) + { + return await InvokeAsync>(nameof(LocationService), + nameof(SuggestLocations), QueryResultsMaxSizeLimit, cancellationToken, url, location, text, + categories, requestOptions); + } + +#endregion +} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Model/LogicComponent.cs b/src/dymaptic.GeoBlazor.Core/Model/LogicComponent.cs index 5f48ec070..312f4e63d 100644 --- a/src/dymaptic.GeoBlazor.Core/Model/LogicComponent.cs +++ b/src/dymaptic.GeoBlazor.Core/Model/LogicComponent.cs @@ -1,20 +1,23 @@ -namespace dymaptic.GeoBlazor.Core.Model; +using System.Runtime.CompilerServices; + + +namespace dymaptic.GeoBlazor.Core.Model; /// /// A base class for non-map components, such as GeometryEngine, Projection, etc. /// -public abstract class LogicComponent : IDisposable +public abstract class LogicComponent( + IAppValidator appValidator, + IJSRuntime jsRuntime, + JsModuleManager jsModuleManager, + AuthenticationManager authenticationManager) { /// - /// Default constructor + /// The maximum size of query results that will be returned in a stream. Note that setting this to a smaller value + /// might create errors in query returns. /// - /// - /// Injected Identity Manager reference - /// - protected LogicComponent(AuthenticationManager authenticationManager) - { - AuthenticationManager = authenticationManager; - } + [Parameter] + public long QueryResultsMaxSizeLimit { get; set; } = 1_000_000_000L; /// /// The name of the logic component. @@ -30,8 +33,7 @@ protected LogicComponent(AuthenticationManager authenticationManager) /// A .NET Object reference to this class for use in JavaScript. /// [JsonConverter(typeof(DotNetObjectReferenceJsonConverter))] - protected DotNetObjectReference DotNetComponentReference => - DotNetObjectReference.Create(this); + protected DotNetObjectReference DotNetComponentReference => DotNetObjectReference.Create(this); /// /// The project library which houses this particular logic component. @@ -39,12 +41,14 @@ protected LogicComponent(AuthenticationManager authenticationManager) protected virtual string Library => "Core"; /// - /// Disposes of the Logic Component and cancels all external calls + /// Boolean flag to identify if GeoBlazor is running in Blazor Server mode /// - public void Dispose() - { - CancellationTokenSource.Dispose(); - } + protected bool IsServer => jsRuntime.GetType().Name.Contains("Remote"); + + /// + /// The AuthenticationManager instance. + /// + protected AuthenticationManager AuthenticationManager => authenticationManager; /// /// A JavaScript invokable method that returns a JS Error and converts it to an Exception. @@ -59,38 +63,47 @@ public void Dispose() public void OnJavascriptError(JavascriptError error) { var exception = new JavascriptException(error); + throw exception; } /// /// Initializes the JavaScript reference component, if not already initialized. /// - public virtual async Task Initialize() + /// + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + public virtual async Task Initialize(CancellationToken cancellationToken = default) { - if (Component is null) + if (!_validated) { - await AuthenticationManager.Initialize(); - IJSObjectReference module = await AuthenticationManager.GetCoreJsModule(); - - Component = await module.InvokeAsync($"get{ComponentName}Wrapper", - CancellationTokenSource.Token, DotNetComponentReference); + await appValidator.ValidateLicense(); + _validated = true; } + + await authenticationManager.Initialize(); + + Component ??= await jsModuleManager.GetLogicComponent(jsRuntime, ComponentName, Library, cancellationToken); } /// /// Convenience method to invoke a JS function from the .NET logic component class. /// + /// + /// The name of the calling class. + /// /// /// The name of the JS function to call. /// /// /// The collection of parameters to pass to the JS call. /// - protected virtual async Task InvokeVoidAsync(string method, params object?[] parameters) + protected internal virtual async Task InvokeVoidAsync(string className, [CallerMemberName] string method = "", + params object?[] parameters) { await Initialize(); - await Component!.InvokeVoidAsync(method, CancellationTokenSource.Token, parameters); + await Component!.InvokeVoidJsMethod(IsServer, method, className, CancellationToken.None, parameters); } /// @@ -99,21 +112,11 @@ protected virtual async Task InvokeVoidAsync(string method, params object?[] par /// /// The name of the JS function to call. /// - /// - /// The collection of parameters to pass to the JS call. + /// + /// The name of the calling class. /// - protected virtual async Task InvokeAsync(string method, params object?[] parameters) - { - await Initialize(); - - return await Component!.InvokeAsync(method, CancellationTokenSource.Token, parameters); - } - - /// - /// Convenience method to invoke a JS function from the .NET logic component class. - /// - /// - /// The name of the JS function to call. + /// + /// The maximum size of query results that will be returned in a stream. /// /// /// The CancellationToken to cancel an asynchronous operation. @@ -121,20 +124,14 @@ protected virtual async Task InvokeAsync(string method, params object?[] p /// /// The collection of parameters to pass to the JS call. /// - protected virtual async Task InvokeAsync(string method, CancellationToken cancellationToken, params object?[] parameters) + protected internal virtual async Task InvokeAsync(string className, [CallerMemberName] string method = "", + long? maxAllowedSize = null, CancellationToken cancellationToken = default, params object?[] parameters) { - await Initialize(); + await Initialize(cancellationToken); - return await Component!.InvokeAsync(method, cancellationToken, parameters); + return await Component!.InvokeJsMethod(IsServer, method, className, maxAllowedSize, cancellationToken, + parameters); } - - /// - /// The reference to the Authentication Manager. - /// - protected readonly AuthenticationManager AuthenticationManager; - /// - /// Creates a cancellation token to control external calls - /// - protected readonly CancellationTokenSource CancellationTokenSource = new(); + private bool _validated; } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Model/MapColor.cs b/src/dymaptic.GeoBlazor.Core/Model/MapColor.cs index 28ac417ab..3a2ef3350 100644 --- a/src/dymaptic.GeoBlazor.Core/Model/MapColor.cs +++ b/src/dymaptic.GeoBlazor.Core/Model/MapColor.cs @@ -9,7 +9,7 @@ namespace dymaptic.GeoBlazor.Core.Model; [JsonConverter(typeof(MapColorConverter))] [CodeGenerationIgnore] [ProtobufSerializable] -public class MapColor : IEquatable, IProtobufSerializable +public class MapColor : IEquatable, IEnumerable, IProtobufSerializable { /// /// Parameterless constructor for Protobuf deserialization. @@ -25,9 +25,9 @@ public MapColor() /// /// Requires 3 or 4 values, in the order R(0-255), G(0-255), B(0-255), A(0-1). A is optional. /// - public MapColor(params double[] values) + public MapColor(params IReadOnlyList values) { - RgbaValues = values; + RgbaValues = values.ToArray(); } /// @@ -57,20 +57,53 @@ public MapColor(string hexOrNameValue) return !Equals(left, right); } + /// + /// Implicitly converts a color hex or name value to a GeoBlazor instance. + /// + public static implicit operator MapColor(string hexOrNameValue) => new(hexOrNameValue); + + /// + /// Implicitly converts a GeoBlazor instance to a hex or name value. + /// + public static implicit operator string?(MapColor color) => color.HexOrNameValue ?? color.ToHex(); + + /// + /// Implicitly converts a numeric array to a GeoBlazor instance. + /// + public static implicit operator MapColor(double[] rgbaValues) => new(rgbaValues); + + /// + /// Implicitly converts a numeric array to a GeoBlazor instance. + /// + public static implicit operator MapColor(List rgbaValues) => new(rgbaValues); + /// /// The numeric values for calculating a color (rgb/rgba). /// public double[]? RgbaValues { - get => _rgbaValues; - set + get + { + if (_rgbaValues is null) + { + Color? color = ToSystemColor(); + + if (color is not null) + { + _rgbaValues = [color.Value.R, color.Value.G, color.Value.B, color.Value.A / 255.0]; + } + } + + return _rgbaValues; + } + private init { _rgbaValues = value; Color? color = ToSystemColor(); if (color is not null && (HexOrNameValue is null || HexOrNameValue.Length == 0)) { - HexOrNameValue = ToHex(); + _hexOrNameValue = ToHex(); } } } @@ -80,8 +113,13 @@ public double[]? RgbaValues /// public string? HexOrNameValue { - get => _hexOrNameValue; - set + get + { + _hexOrNameValue ??= ToHex(); + + return _hexOrNameValue; + } + private init { _hexOrNameValue = value; Color? color = ToSystemColor(); @@ -93,6 +131,14 @@ public string? HexOrNameValue } } + /// + /// Provides support for Collection Expressions. + /// + public IEnumerator GetEnumerator() + { + return _rgbaValues?.GetEnumerator() ?? Enumerable.Empty().GetEnumerator(); + } + /// public bool Equals(MapColor? other) { @@ -137,17 +183,6 @@ public MapColorSerializationRecord ToProtobuf() /// public static MapColor? BlendColors(MapColor start, MapColor end, double weight) { - if (start.RgbaValues?.Any() != true) - { - // reset triggers calculation of rgba values from hex or name - start.HexOrNameValue = start.HexOrNameValue; - } - - if (end.RgbaValues?.Any() != true) - { - end.HexOrNameValue = end.HexOrNameValue; - } - if (start.RgbaValues?.Any() == true && end.RgbaValues?.Any() == true) { double[] startValues = start.RgbaValues.ToArray(); @@ -187,6 +222,14 @@ public override int GetHashCode() return HashCode.Combine(RgbaValues, HexOrNameValue); } + /// + /// For internal use only, used to support Collection Expressions and implicit conversions from arrays/lists. + /// + public void Add(double val) + { + _rgbaValues = _rgbaValues is null ? [val] : [.._rgbaValues, val]; + } + /// /// Clones the color object. /// diff --git a/src/dymaptic.GeoBlazor.Core/Model/PortalFeaturedGroups.cs b/src/dymaptic.GeoBlazor.Core/Model/PortalFeaturedGroups.cs new file mode 100644 index 000000000..2ea95db9d --- /dev/null +++ b/src/dymaptic.GeoBlazor.Core/Model/PortalFeaturedGroups.cs @@ -0,0 +1,16 @@ +namespace dymaptic.GeoBlazor.Core.Model; + +/// +/// GeoBlazor Docs +/// The featured groups for the portal. +/// ArcGIS Maps SDK for JavaScript +/// +/// +/// Name of the group owner. +/// ArcGIS Maps SDK for JavaScript +/// +/// +/// Group title. +/// ArcGIS Maps SDK for JavaScript +/// +public record PortalFeaturedGroups(string Owner, string Title); \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Model/ProjectionEngine.cs b/src/dymaptic.GeoBlazor.Core/Model/ProjectionEngine.cs index f75062fe5..49cb52126 100644 --- a/src/dymaptic.GeoBlazor.Core/Model/ProjectionEngine.cs +++ b/src/dymaptic.GeoBlazor.Core/Model/ProjectionEngine.cs @@ -5,19 +5,13 @@ /// ArcGIS Maps SDK for JavaScript /// [CodeGenerationIgnore] -public class ProjectionEngine : LogicComponent +public class ProjectionEngine( + IAppValidator appValidator, + IJSRuntime jsRuntime, + JsModuleManager jsModuleManager, + AuthenticationManager authenticationManager) + : LogicComponent(appValidator, jsRuntime, jsModuleManager, authenticationManager) { - /// - /// Default Constructor - /// - /// - /// Injected Identity Manager reference - /// - public ProjectionEngine(AuthenticationManager authenticationManager) : - base(authenticationManager) - { - } - /// protected override string ComponentName => nameof(ProjectionEngine); @@ -33,9 +27,16 @@ public ProjectionEngine(AuthenticationManager authenticationManager) : /// /// A collection of projected geometries. /// - public async Task Project(Geometry[] geometries, SpatialReference spatialReference) + /// The cancellation token to use for the operation. + public Task Project(Geometry[] geometries, SpatialReference spatialReference, + CancellationToken cancellationToken = default) { - return await InvokeAsync("project", geometries, spatialReference, null); + if (geometries.Length == 0) + { + return Task.FromResult([]); + } + + return Project(geometries, spatialReference, null, cancellationToken); } /// @@ -53,11 +54,13 @@ public ProjectionEngine(AuthenticationManager authenticationManager) : /// /// A collection of projected geometries. /// + /// The cancellation token to use for the operation. + [SerializedMethod] public async Task Project(Geometry[] geometries, SpatialReference spatialReference, - GeographicTransformation? geographicTransformation) + GeographicTransformation? geographicTransformation, CancellationToken cancellationToken = default) { - return await InvokeAsync("project", geometries, spatialReference, - geographicTransformation); + return await InvokeAsync(nameof(ProjectionEngine), nameof(Project), + QueryResultsMaxSizeLimit, cancellationToken, geometries, spatialReference, geographicTransformation); } /// @@ -72,9 +75,17 @@ public ProjectionEngine(AuthenticationManager authenticationManager) : /// /// A projected geometry. /// - public async Task Project(Geometry geometry, SpatialReference spatialReference) + /// The cancellation token to use for the operation. + public Task Project(Geometry geometry, SpatialReference spatialReference, + CancellationToken cancellationToken = default) { - return await InvokeAsync("project", geometry, spatialReference, null); + if (spatialReference.Equals(geometry.SpatialReference)) + { + // no conversion, just return the same geometry + return Task.FromResult(geometry); + } + + return Project(geometry, spatialReference, null, cancellationToken); } /// @@ -92,11 +103,13 @@ public ProjectionEngine(AuthenticationManager authenticationManager) : /// /// A projected geometry. /// + /// The cancellation token to use for the operation. + [SerializedMethod] public async Task Project(Geometry geometry, SpatialReference spatialReference, - GeographicTransformation? geographicTransformation) + GeographicTransformation? geographicTransformation, CancellationToken cancellationToken = default) { - return await InvokeAsync("project", geometry, spatialReference, - geographicTransformation); + return await InvokeAsync(nameof(ProjectionEngine), nameof(Project), + QueryResultsMaxSizeLimit, cancellationToken, geometry, spatialReference, geographicTransformation); } /// @@ -114,10 +127,13 @@ public ProjectionEngine(AuthenticationManager authenticationManager) : /// /// A geographic transformation. /// + /// The cancellation token to use for the operation. + [SerializedMethod] public async Task GetTransformation(SpatialReference inSpatialReference, - SpatialReference outSpatialReference, Extent extent) + SpatialReference outSpatialReference, Extent extent, CancellationToken cancellationToken = default) { - return await InvokeAsync("getTransformation", inSpatialReference, + return await InvokeAsync(nameof(ProjectionEngine), nameof(GetTransformation), + QueryResultsMaxSizeLimit, cancellationToken, inSpatialReference, outSpatialReference, extent); } @@ -136,10 +152,13 @@ public ProjectionEngine(AuthenticationManager authenticationManager) : /// /// A collection of geographic transformation. /// + /// The cancellation token to use for the operation. + [SerializedMethod] public async Task GetTransformations(SpatialReference inSpatialReference, - SpatialReference outSpatialReference, Extent extent) + SpatialReference outSpatialReference, Extent extent, CancellationToken cancellationToken = default) { - return await InvokeAsync("getTransformations", - inSpatialReference, outSpatialReference, extent); + return await InvokeAsync(nameof(ProjectionEngine), + nameof(GetTransformations), QueryResultsMaxSizeLimit, cancellationToken, inSpatialReference, + outSpatialReference, extent); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Results/NearestPointResult.cs b/src/dymaptic.GeoBlazor.Core/Results/ProximityResult.cs similarity index 68% rename from src/dymaptic.GeoBlazor.Core/Results/NearestPointResult.cs rename to src/dymaptic.GeoBlazor.Core/Results/ProximityResult.cs index fc9046829..9c4237fa0 100644 --- a/src/dymaptic.GeoBlazor.Core/Results/NearestPointResult.cs +++ b/src/dymaptic.GeoBlazor.Core/Results/ProximityResult.cs @@ -4,7 +4,7 @@ namespace dymaptic.GeoBlazor.Core.Results; /// Object returned from the nearestCoordinate(), nearestVertex(), and nearestVertices() methods of . /// [CodeGenerationIgnore] -public record NearestPointResult +public record ProximityResult { /// /// A vertex within the specified distance of the search. @@ -25,4 +25,12 @@ public record NearestPointResult /// Indicates if it is an empty geometry. /// public bool IsEmpty { get; set; } + + /// + /// Indicates if the nearest coordinate is on the right side or left side of the input point. + /// + /// + /// Note: This property will only be present when calculateLeftRightSide is set to true when calling getNearestCoordinate(). + /// + public bool IsRightSide { get; set; } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/arcGisJsInterop.ts b/src/dymaptic.GeoBlazor.Core/Scripts/arcGisJsInterop.ts index bb16b6404..29994148e 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/arcGisJsInterop.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/arcGisJsInterop.ts @@ -44,8 +44,6 @@ import Polyline from "@arcgis/core/geometry/Polyline"; import Popup from "@arcgis/core/widgets/Popup"; import PopupTemplate from "@arcgis/core/PopupTemplate"; import Portal from "@arcgis/core/portal/Portal"; -import * as promiseUtils from "@arcgis/core/core/promiseUtils"; -import * as projectionEngine from "@arcgis/core/geometry/projection"; import Query from "@arcgis/core/rest/support/Query"; import RasterStretchRenderer from "@arcgis/core/renderers/RasterStretchRenderer"; import RouteParameters from "@arcgis/core/rest/support/RouteParameters"; @@ -57,8 +55,8 @@ import TileLayer from "@arcgis/core/layers/TileLayer"; import View from "@arcgis/core/views/View"; import WebMap from "@arcgis/core/WebMap"; import Widget from "@arcgis/core/widgets/Widget"; -import {load, parse } from "protobufjs"; -import { protoTypeDefinitions } from "./geoblazorProto"; +import {load, parse} from "protobufjs"; +import {protoTypeDefinitions} from "./geoblazorProto"; import {buildDotNetExtent, buildJsExtent} from './extent'; import {buildJsGraphic} from './graphic'; import {buildDotNetLayer, buildJsLayer, buildJsLayerWrapper} from './layer'; @@ -75,23 +73,23 @@ import ColorBackground from "@arcgis/core/webmap/background/ColorBackground"; import {buildJsColor} from './mapColor'; import {buildJsBasemap} from "./basemap"; import GeoJSONLayer from "@arcgis/core/layers/GeoJSONLayer"; -import ScreenPoint = __esri.ScreenPoint; import { + addHeadLink, arcGisObjectRefs, - dotNetRefs, - jsObjectRefs, + base64ToArrayBuffer, buildJsStreamReference, + checkHeadLink, + dotNetRefs, generateSerializableJson, + jsObjectRefs, + logUncaughtError, + lookupGeoBlazorId, + removeHeadLink, sanitize, setCursor, - base64ToArrayBuffer, - setProperty, - addHeadLink, - checkHeadLink, - removeHeadLink, - lookupGeoBlazorId, - logUncaughtError + setProperty } from "./geoBlazorCore"; +import ScreenPoint = __esri.ScreenPoint; // endregion // region exports @@ -112,8 +110,7 @@ export { buildJsGraphic, buildJsSymbol, reactiveUtils, - geometryEngine, - projectionEngine + geometryEngine }; export const popupTemplateRefs: Record = {}; @@ -230,8 +227,6 @@ export async function buildArcGisMapView(abortSignal: AbortSignal, id: string, d loadProtobuf(); } - await projectionEngine.load(); - checkConnectivity(id); if (abortSignal.aborted) { @@ -1230,7 +1225,7 @@ export function setGraphicSymbol(id: string, symbol: any, layerId: string | null export function getGraphicSymbol(id: string, layerId: string | null, viewId: string | null): any { const graphic = lookupJsGraphicById(id, layerId, viewId); if (hasValue(graphic?.symbol)) { - return buildDotNetSymbol(graphic!.symbol!, viewId); + return buildDotNetSymbol(graphic!.symbol!); } return null; @@ -1862,7 +1857,6 @@ export function loadProtobuf(): void { GraphicCollectionSerializationRecord = ProtoTypes["GraphicCollection"]; // TODO: UNCOMMENT: // ProtoViewHitCollection = ProtoTypes["ViewHitCollection"]; - console.debug('Protobuf graphic types initialized'); // TODO: REMOVE WHEN NOT NEEDED: load("_content/dymaptic.GeoBlazor.Core/graphic.json", function (err, root) { @@ -1871,7 +1865,6 @@ export function loadProtobuf(): void { } ProtoGraphicCollection = root?.lookupType("ProtoGraphicCollection"); ProtoViewHitCollection = root?.lookupType("ProtoViewHitCollection"); - console.debug('Protobuf graphics json loaded'); }); } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/baseComponent.ts b/src/dymaptic.GeoBlazor.Core/Scripts/baseComponent.ts index 4f67987e9..590db283b 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/baseComponent.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/baseComponent.ts @@ -84,6 +84,11 @@ export default class BaseComponent implements IPropertyWrapper { return paramValue; } + if (paramType == 'abortSignal') { + // abortSignal is a JSObjectReference as well + return paramValue; + } + if (Array.isArray(paramValue)) { let arrayValues: any[] = []; for (let i = 0; i < paramValue.length; i++) { @@ -294,6 +299,15 @@ export default class BaseComponent implements IPropertyWrapper { } } + createAbortControllerAndSignal() { + const controller = new AbortController(); + const signal = controller.signal; + return { + abortControllerRef: DotNet.createJSObjectReference(controller), + abortSignalRef: DotNet.createJSObjectReference(signal) + } + } + simpleDotNetTypes = ['int32', 'int64', 'long', 'decimal', 'double', 'single', 'float', 'int', 'bool', 'ulong', 'uint', 'ushort', 'byte', 'sbyte', 'char', 'string', 'datetime', 'dateonly', 'timeonly', 'guid', 'datetimeoffset', 'timespan']; diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/cSVLayer.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/cSVLayer.gb.ts index 773015fac..0a0b5d9b7 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/cSVLayer.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/cSVLayer.gb.ts @@ -465,7 +465,7 @@ export default class CSVLayerGenerated extends BaseComponent { } let { buildDotNetLabel } = await import('./label'); - return await Promise.all(this.layer.labelingInfo!.map(async i => await buildDotNetLabel(i, this.layerId, this.viewId))); + return await Promise.all(this.layer.labelingInfo!.map(async i => await buildDotNetLabel(i))); } async setLabelingInfo(value: any): Promise { @@ -953,7 +953,7 @@ export async function buildDotNetCSVLayerGenerated(jsObject: any, layerId: strin if (hasValue(jsObject.labelingInfo)) { let { buildDotNetLabel } = await import('./label'); - dotNetCSVLayer.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i, layerId, viewId))); + dotNetCSVLayer.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i))); } if (hasValue(jsObject.orderBy)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/customPopupContent.ts b/src/dymaptic.GeoBlazor.Core/Scripts/customPopupContent.ts index 7c279b01b..3737dddde 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/customPopupContent.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/customPopupContent.ts @@ -1,12 +1,99 @@ -// PLACEHOLDER FOR GEOBLAZOR PRO IMPLEMENTATION +import { + arcGisObjectRefs, + buildJsStreamReference, + hasValue, + jsObjectRefs, + lookupGeoBlazorId, + removeCircularReferences, + Pro +} from './geoBlazorCore'; +import {buildDotNetGraphic} from "./graphic"; +import CustomContent from "@arcgis/core/popup/content/CustomContent"; +import {buildJsWidget} from "./widget"; -import { sanitize } from './geoBlazorCore'; - -// don't remove the layerId and viewId parameters, they are used in the Pro implementation export function buildJsCustomPopupContent(dotNetObject: any, layerId: string | null, viewId: string | null): any { - return sanitize(dotNetObject); + if (!hasValue(dotNetObject)) { + return null; + } + + if (!Pro) { + throw new Error('Custom Popup Content only available with GeoBlazor Pro.'); + } + + let customContentDiv = document.getElementById(`custom-content-${dotNetObject.id}`) as HTMLElement; + if (!hasValue(customContentDiv)) { + return null; + } + + // remove the custom content div from the DOM + customContentDiv.parentElement?.removeChild(customContentDiv); + customContentDiv.hidden = false; + + // remove comment nodes + for (let i = 0; i < customContentDiv.childNodes.length; i++) { + let childNode = customContentDiv.childNodes[i]; + if (childNode.nodeType === 8) { + customContentDiv.removeChild(childNode); + i--; + } + } + + let properties: any = {}; + if (hasValue(dotNetObject.hasContentCreatorFunction) && dotNetObject.hasContentCreatorFunction) { + properties.creator = async (event) => { + let dnGraphic = buildDotNetGraphic(event.graphic, layerId, viewId); + let dnStream = buildJsStreamReference({graphic: dnGraphic}); + return await dotNetObject.dotNetComponentReference.invokeMethodAsync('OnJsPopupTemplateContentCreator', dnStream); + }; + } else if (hasValue(dotNetObject.widgetContent)) { + properties.creator = async () => { + return await buildJsWidget(dotNetObject.widgetContent, layerId, viewId); + } + } else { + properties.creator = () => { + return customContentDiv; + } + } + + if (hasValue(dotNetObject.hasDestroyerFunction) && dotNetObject.hasDestroyerFunction) { + properties.destroyer = async (event) => { + let dnGraphic = buildDotNetGraphic(event.graphic, layerId, viewId); + await dotNetObject.dotNetComponentReference.invokeMethodAsync('OnJsDestroyer', {graphic: dnGraphic}); + }; + } + + if (hasValue(dotNetObject.outFields) && dotNetObject.outFields.length > 0) { + properties.outFields = dotNetObject.outFields; + } + + let jsCustomContent = new CustomContent(properties); + + jsObjectRefs[dotNetObject.id] = jsCustomContent; + arcGisObjectRefs[dotNetObject.id] = jsCustomContent; + + return jsCustomContent; } export function buildDotNetCustomPopupContent(jsObject: any): any { - return jsObject; + if (!hasValue(jsObject)) { + return null; + } + + let dotNetCustomPopupContent: any = {}; + + if (hasValue(jsObject.outFields)) { + dotNetCustomPopupContent.outFields = jsObject.outFields; + } + + if (hasValue(jsObject.type)) { + dotNetCustomPopupContent.type = removeCircularReferences(jsObject.type); + } + + + let geoBlazorId = lookupGeoBlazorId(jsObject); + if (hasValue(geoBlazorId)) { + dotNetCustomPopupContent.id = geoBlazorId; + } + + return dotNetCustomPopupContent; } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/featureLayer.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/featureLayer.gb.ts index e450b56f9..91c5a27ba 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/featureLayer.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/featureLayer.gb.ts @@ -551,7 +551,7 @@ export default class FeatureLayerGenerated extends BaseComponent { } let { buildDotNetLabel } = await import('./label'); - return await Promise.all(this.layer.labelingInfo!.map(async i => await buildDotNetLabel(i, this.layerId, this.viewId))); + return await Promise.all(this.layer.labelingInfo!.map(async i => await buildDotNetLabel(i))); } async setLabelingInfo(value: any): Promise { @@ -1188,7 +1188,7 @@ export async function buildDotNetFeatureLayerGenerated(jsObject: any, layerId: s if (hasValue(jsObject.labelingInfo)) { let { buildDotNetLabel } = await import('./label'); - dotNetFeatureLayer.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i, layerId, viewId))); + dotNetFeatureLayer.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i))); } if (hasValue(jsObject.orderBy)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/featureLayer.ts b/src/dymaptic.GeoBlazor.Core/Scripts/featureLayer.ts index f114546ac..96ed6d5ed 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/featureLayer.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/featureLayer.ts @@ -23,8 +23,8 @@ import { } from './geoBlazorCore'; import Graphic from "@arcgis/core/Graphic"; import {buildDotNetPopupTemplate} from './popupTemplate'; -import CreatePopupTemplateOptions = __esri.CreatePopupTemplateOptions; import {buildJsGraphic} from "./graphic"; +import CreatePopupTemplateOptions = __esri.CreatePopupTemplateOptions; export default class FeatureLayerWrapper extends FeatureLayerGenerated { @@ -141,7 +141,7 @@ export default class FeatureLayerWrapper extends FeatureLayerGenerated { queryId: string): Promise { try { let { buildJsTopFeaturesQuery} = await import('./topFeaturesQuery'); - let jsQuery = await buildJsTopFeaturesQuery(query, this.layerId, this.viewId); + let jsQuery = await buildJsTopFeaturesQuery(query); let featureSet = await this.layer.queryTopFeatures(jsQuery, options); let {buildDotNetFeatureSet} = await import('./featureSet'); let dotNetFeatureSet = await buildDotNetFeatureSet(featureSet, this.geoBlazorId, this.viewId); @@ -159,20 +159,20 @@ export default class FeatureLayerWrapper extends FeatureLayerGenerated { async queryTopFeatureCount(query: DotNetTopFeaturesQuery, options: any): Promise { let { buildJsTopFeaturesQuery} = await import('./topFeaturesQuery'); - let jsQuery = await buildJsTopFeaturesQuery(query, this.layerId, this.viewId); + let jsQuery = await buildJsTopFeaturesQuery(query); return await this.layer.queryTopFeatureCount(jsQuery, options); } async queryTopObjectIds(query: DotNetTopFeaturesQuery, options: any): Promise { let { buildJsTopFeaturesQuery} = await import('./topFeaturesQuery'); - let jsQuery = await buildJsTopFeaturesQuery(query, this.layerId, this.viewId); + let jsQuery = await buildJsTopFeaturesQuery(query); let result = await this.layer.queryTopObjectIds(jsQuery, options); return result; } async queryTopFeaturesExtent(query: DotNetTopFeaturesQuery, options: any): Promise { let { buildJsTopFeaturesQuery} = await import('./topFeaturesQuery'); - let jsQuery = await buildJsTopFeaturesQuery(query, this.layerId, this.viewId); + let jsQuery = await buildJsTopFeaturesQuery(query); let result = await this.layer.queryTopFeaturesExtent(jsQuery, options); let {buildDotNetExtent} = await import('./extent'); return { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/geoBlazorCore.ts b/src/dymaptic.GeoBlazor.Core/Scripts/geoBlazorCore.ts index 8339d6def..84a81d06a 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/geoBlazorCore.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/geoBlazorCore.ts @@ -1,18 +1,13 @@ import { + actionHandlers, addArcGisLayer, - graphicsRefs, buildArcGisMapView, - loadProtobuf, - GraphicCollectionSerializationRecord, - popupTemplateRefs, - actionHandlers, esriConfig, + graphicsRefs, + popupTemplateRefs, resetMapComponent } from './arcGisJsInterop'; import AuthenticationManager from "./authenticationManager"; -import ProjectionWrapper from "./projection"; -import GeometryEngineWrapper from "./geometryEngine"; -import LocatorWrapper from "./locationService"; import MapViewWrapper from "./mapView"; // backwards-compatibility re-export, since everything used to be in this module @@ -571,6 +566,22 @@ export function buildEncodedJson(object: any): Uint8Array { return encoder.encode(json!); } +export function getDefaultClassInstanceFromModule(module: any) { + if (module === null || module === undefined) { + return null; + } + if (module.default !== undefined) { + return new module.default(); + } + // find the first class in the module + for (const key in module) { + if (typeof module[key] === 'function') { + return new module[key](); + } + } + return null; +} + // Converts a base64 string to an ArrayBuffer export function base64ToArrayBuffer(base64): Uint8Array { const binaryString = atob(base64); @@ -594,26 +605,4 @@ export function getAuthenticationManager(dotNetRef: any, apiKey: string | null, return _authenticationManager; } -export async function getProjectionEngineWrapper(): Promise { - if (GraphicCollectionSerializationRecord === undefined) { - loadProtobuf(); - } - return new ProjectionWrapper(); -} - -export async function getGeometryEngineWrapper(): Promise { - if (GraphicCollectionSerializationRecord === undefined) { - loadProtobuf(); - } - return new GeometryEngineWrapper(); -} - -export async function getLocationServiceWrapper(): Promise { - if (GraphicCollectionSerializationRecord === undefined) { - loadProtobuf(); - } - - return new LocatorWrapper(); -} - // endregion \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/geoJSONLayer.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/geoJSONLayer.gb.ts index 1e9b5f060..d67650230 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/geoJSONLayer.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/geoJSONLayer.gb.ts @@ -455,7 +455,7 @@ export default class GeoJSONLayerGenerated extends BaseComponent { } let { buildDotNetLabel } = await import('./label'); - return await Promise.all(this.layer.labelingInfo!.map(async i => await buildDotNetLabel(i, this.layerId, this.viewId))); + return await Promise.all(this.layer.labelingInfo!.map(async i => await buildDotNetLabel(i))); } async setLabelingInfo(value: any): Promise { @@ -927,7 +927,7 @@ export async function buildDotNetGeoJSONLayerGenerated(jsObject: any, layerId: s if (hasValue(jsObject.labelingInfo)) { let { buildDotNetLabel } = await import('./label'); - dotNetGeoJSONLayer.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i, layerId, viewId))); + dotNetGeoJSONLayer.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i))); } if (hasValue(jsObject.orderBy)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/geometry.ts b/src/dymaptic.GeoBlazor.Core/Scripts/geometry.ts index 0bb27900e..c7ebc5c98 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/geometry.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/geometry.ts @@ -4,7 +4,10 @@ import {buildDotNetPolyline, buildJsPolyline} from "./polyline"; import {buildDotNetExtent, buildJsExtent} from "./extent"; import {buildDotNetMultipoint, buildJsMultipoint} from "./multipoint"; import {buildDotNetMesh, buildJsMesh} from "./mesh"; -import {hasValue} from './geoBlazorCore'; +import { + hasValue, + removeCircularReferences, +} from './geoBlazorCore'; export function buildDotNetGeometry(geometry: any): any { if (!hasValue(geometry)) { @@ -23,6 +26,9 @@ export function buildDotNetGeometry(geometry: any): any { return buildDotNetMultipoint(geometry); case "mesh": return buildDotNetMesh(geometry); + default: + // unknown type + return removeCircularReferences(geometry); } } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/geometryEngine.ts b/src/dymaptic.GeoBlazor.Core/Scripts/geometryEngine.ts index 1d07e3e01..f27dccb9f 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/geometryEngine.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/geometryEngine.ts @@ -3,33 +3,24 @@ import Polygon from "@arcgis/core/geometry/Polygon"; import Polyline from "@arcgis/core/geometry/Polyline"; import SpatialReference from "@arcgis/core/geometry/SpatialReference"; import Point from "@arcgis/core/geometry/Point"; -import { - DotNetExtent, - DotNetGeometry, - DotNetPoint, - DotNetPolygon, - DotNetPolyline -} from "./definitions"; +import {DotNetExtent, DotNetGeometry, DotNetPoint, DotNetPolygon, DotNetPolyline} from "./definitions"; import Extent from "@arcgis/core/geometry/Extent"; import {buildDotNetExtent, buildJsExtent} from "./extent"; import {buildDotNetPolygon, buildJsPolygon} from "./polygon"; import {buildDotNetGeometry, buildJsGeometry} from "./geometry"; import {buildDotNetPoint, buildJsPoint} from "./point"; -import {buildDotNetPolyline, buildJsPolyline} from "./polyline"; +import {buildDotNetPolyline, buildJsPathsOrRings, buildJsPolyline} from "./polyline"; +import Mesh from "@arcgis/core/geometry/Mesh"; +import Multipoint from "@arcgis/core/geometry/Multipoint"; +import {hasValue} from './geoBlazorCore'; +import BaseComponent from "./baseComponent"; import LinearUnits = __esri.LinearUnits; import SpatialReferenceInfo = __esri.SpatialReferenceInfo; import AreaUnits = __esri.AreaUnits; -import Mesh from "@arcgis/core/geometry/Mesh"; -import Multipoint from "@arcgis/core/geometry/Multipoint"; -import { hasValue } from './geoBlazorCore'; import GeometryUnion = __esri.GeometryUnion; +import {AreaUnit, LengthUnit} from "@arcgis/core/core/units"; -export default class GeometryEngineWrapper { - private readonly returnToDotNet: boolean; - - constructor(returnToDotNet: boolean = true) { - this.returnToDotNet = returnToDotNet; - } +export default class GeometryEngineWrapper extends BaseComponent { async buffer(geometries: DotNetGeometry | Array, distances: number | Array, unit: LinearUnits | null, unionResults: boolean | null): Promise { @@ -47,7 +38,7 @@ export default class GeometryEngineWrapper { options.union = unionResults; } jsBuffer = bufferOperator.executeMany(jsGeometries as GeometryUnion[], distances as number[], options); - return this.returnToDotNet ? jsBuffer.map(g => buildDotNetPolygon(g) as DotNetPolygon) : jsBuffer; + return jsBuffer.map(g => buildDotNetPolygon(g) as DotNetPolygon); } jsGeometries = buildJsGeometry(geometries) as Geometry; @@ -56,22 +47,20 @@ export default class GeometryEngineWrapper { } jsBuffer = bufferOperator.execute(jsGeometries as GeometryUnion, distances as number, options) as Polygon; - return this.returnToDotNet ? buildDotNetPolygon(jsBuffer) : jsBuffer; + return buildDotNetPolygon(jsBuffer); } async clip(geometry: DotNetGeometry, extent: DotNetExtent): Promise { let clipOperator = await import('@arcgis/core/geometry/operators/clipOperator'); let jsClip = clipOperator.execute(buildJsGeometry(geometry) as any, buildJsExtent(extent, null)); - return this.returnToDotNet ? buildDotNetGeometry(jsClip) : jsClip; - + return buildDotNetGeometry(jsClip); } async contains(containerGeometry: DotNetGeometry, insideGeometry: DotNetGeometry): Promise { let containsOperator = await import('@arcgis/core/geometry/operators/containsOperator'); return containsOperator.execute(buildJsGeometry(containerGeometry) as GeometryUnion, buildJsGeometry(insideGeometry) as GeometryUnion); - } async convexHull(geometries: Array | DotNetGeometry, merge: boolean | null): Promise { @@ -86,42 +75,58 @@ export default class GeometryEngineWrapper { options.merge = merge; } jsHull = convexHullOperator.executeMany(jsGeometries as GeometryUnion[], options) as GeometryUnion[]; - return this.returnToDotNet ? jsHull.map(g => buildDotNetGeometry(g) as DotNetGeometry) : jsHull; + return jsHull.map(g => buildDotNetGeometry(g) as DotNetGeometry); } jsGeometries = buildJsGeometry(geometries) as Geometry; jsHull = convexHullOperator.execute(jsGeometries as any) as GeometryUnion; - return this.returnToDotNet ? buildDotNetGeometry(jsHull) : jsHull; - + return buildDotNetGeometry(jsHull); } async crosses(geometry1: DotNetGeometry, geometry2: DotNetGeometry): Promise { let crossesOperator = await import('@arcgis/core/geometry/operators/crossesOperator'); return crossesOperator.execute(buildJsGeometry(geometry1) as GeometryUnion, buildJsGeometry(geometry2) as GeometryUnion); - } async cut(geometry: DotNetGeometry, cutter: DotNetPolyline): Promise { let cutOperator = await import('@arcgis/core/geometry/operators/cutOperator'); let jsCut = cutOperator.execute(buildJsGeometry(geometry) as GeometryUnion, buildJsPolyline(cutter) as Polyline); - return this.returnToDotNet ? jsCut.map(g => buildDotNetGeometry(g) as DotNetGeometry) : jsCut; - + return jsCut.map(g => buildDotNetGeometry(g) as DotNetGeometry); } - async densify(geometry: DotNetGeometry, maxSegmentLength: number, maxSegmentLengthUnit: LinearUnits | null) + async densify(geometries: DotNetGeometry | Array, maxSegmentLength: number, + maxSegmentLengthUnit: LengthUnit | null) : Promise { let densifyOperator = await import('@arcgis/core/geometry/operators/densifyOperator'); - let jsGeometry = buildJsGeometry(geometry) as GeometryUnion; - let jsDensified: Geometry; let options: any = {}; + let hasOptions = false; if (hasValue(maxSegmentLengthUnit)) { options.unit = maxSegmentLengthUnit; + hasOptions = true; } - jsDensified = densifyOperator.execute(jsGeometry, maxSegmentLength, options); - return this.returnToDotNet ? buildDotNetGeometry(jsDensified) : jsDensified; + if (geometries instanceof Array) { + let jsGeometries: GeometryUnion[] = geometries.map(buildJsGeometry) as GeometryUnion[]; + let jsDensified: Geometry[]; + if (hasOptions) { + jsDensified = densifyOperator.executeMany(jsGeometries, maxSegmentLength, options); + } else { + jsDensified = densifyOperator.executeMany(jsGeometries, maxSegmentLength); + } + + return jsDensified.map(g => buildDotNetGeometry(g) as DotNetGeometry); + } + let jsGeometry = buildJsGeometry(geometries) as GeometryUnion; + let jsDensified: Geometry; + if (hasOptions) { + jsDensified = densifyOperator.execute(jsGeometry, maxSegmentLength, options); + } else { + jsDensified = densifyOperator.execute(jsGeometry, maxSegmentLength); + } + + return buildDotNetGeometry(jsDensified); } async difference(geometries: Array | DotNetGeometry, subtractor: DotNetGeometry) @@ -133,18 +138,17 @@ export default class GeometryEngineWrapper { jsGeometries = []; geometries.forEach(g => (jsGeometries as Array).push(buildJsGeometry(g) as GeometryUnion)); jsDifference = differenceOperator.executeMany(jsGeometries as GeometryUnion[], buildJsGeometry(subtractor) as GeometryUnion); - return this.returnToDotNet ? jsDifference.map(g => buildDotNetGeometry(g) as DotNetGeometry) : jsDifference; + return jsDifference.map(g => buildDotNetGeometry(g) as DotNetGeometry); } jsGeometries = buildJsGeometry(geometries) as GeometryUnion; jsDifference = differenceOperator.execute(jsGeometries as any, buildJsGeometry(subtractor) as GeometryUnion); - return this.returnToDotNet ? buildDotNetGeometry(jsDifference) : jsDifference; + return buildDotNetGeometry(jsDifference); } async disjoint(geometry1: DotNetGeometry, geometry2: DotNetGeometry): Promise { let disjointOperator = await import('@arcgis/core/geometry/operators/disjointOperator'); return disjointOperator.execute(buildJsGeometry(geometry1) as GeometryUnion, buildJsGeometry(geometry2) as GeometryUnion); - } async distance(geometry1: DotNetGeometry, geometry2: DotNetGeometry, distanceUnit: LinearUnits | null) @@ -160,7 +164,7 @@ export default class GeometryEngineWrapper { return distanceOperator.execute(jsGeometry1, jsGeometry2, options); } - async equals(geometry1: DotNetGeometry, geometry2: DotNetGeometry): Promise { + async areEqual(geometry1: DotNetGeometry, geometry2: DotNetGeometry): Promise { let equalsOperator = await import('@arcgis/core/geometry/operators/equalsOperator'); return equalsOperator.execute(buildJsGeometry(geometry1) as GeometryUnion, buildJsGeometry(geometry2) as GeometryUnion); } @@ -171,76 +175,102 @@ export default class GeometryEngineWrapper { return geometryEngine.extendedSpatialReferenceInfo(spatialReference); } - async flipHorizontal(geometry: DotNetGeometry, flipOrigin: DotNetPoint | null): Promise { - let { default: Transformation } = await import('@arcgis/core/geometry/operators/support/Transformation'); - let transformation = new Transformation(); - let affineTransformOperator = await import('@arcgis/core/geometry/operators/affineTransformOperator'); - let jsGeometry = buildJsGeometry(geometry) as Geometry; - let y0 = jsGeometry.extent?.ymin ?? 0; - let y1 = jsGeometry.extent?.ymax ?? 0; - if (hasValue(flipOrigin?.y)) { - // without a flip origin, the flip point is halfway between the min and max y values - let center = y1 - y0; - // with a set y origin point, we need to calculate the distance from the min and max y values - let flipY = flipOrigin!.y; - if (flipY < center) { - y0 = y1 - (flipY * 2); - } else if (flipY > center) { - y1 = y0 + (flipY * 2); - } - } - transformation.flipY(y0, y1); - let jsFlip = affineTransformOperator.execute(jsGeometry as any, transformation); - - return this.returnToDotNet ? buildDotNetGeometry(jsFlip) : jsFlip; + async flipHorizontal(geometries: DotNetGeometry | DotNetGeometry[], flipOrigin: DotNetPoint | null): Promise { + return await this.flipImplementation(geometries, flipOrigin, 'x'); + } + async flipVertical(geometries: DotNetGeometry | DotNetGeometry[], flipOrigin: DotNetPoint | null): Promise { + return await this.flipImplementation(geometries, flipOrigin, 'y'); } - async flipVertical(geometry: DotNetGeometry, flipOrigin: DotNetPoint | null): Promise { + async flipImplementation(geometries: DotNetGeometry | DotNetGeometry[], flipOrigin: DotNetPoint | null, + axis: 'x' | 'y'): Promise { let { default: Transformation } = await import('@arcgis/core/geometry/operators/support/Transformation'); let transformation = new Transformation(); let affineTransformOperator = await import('@arcgis/core/geometry/operators/affineTransformOperator'); - let jsGeometry = buildJsGeometry(geometry) as Geometry; - let x0 = jsGeometry.extent?.xmin ?? 0; - let x1 = jsGeometry.extent?.xmax ?? 0; - if (hasValue(flipOrigin?.x)) { - // without a flip origin, the flip point is halfway between the min and max x values - let center = x1 - x0; - // with a set x origin point, we need to calculate the distance from the min and max x values - let flipX = flipOrigin!.x; - if (flipX < center) { - x0 = x1 - (flipX * 2); - } else if (flipX > center) { - x1 = x0 + (flipX * 2); - } + + let jsGeometries: GeometryUnion[] = []; + if (geometries instanceof Array) { + jsGeometries = geometries.map(buildJsGeometry) as GeometryUnion[]; + } else { + jsGeometries.push(buildJsGeometry(geometries) as GeometryUnion); } - transformation.flipX(x0, x1); - let jsFlip = affineTransformOperator.execute(jsGeometry as any, transformation); - return this.returnToDotNet ? buildDotNetGeometry(jsFlip) : jsFlip; + let extent: Extent = jsGeometries[0].extent as Extent; + for (let i = 0; i < jsGeometries.length; i++) { + let geom = jsGeometries[i]; + if (i > 0) { + extent = extent.union(geom.extent as Extent) as Extent; + } + } + let n0 = axis == 'x' ? extent.xmin : extent.ymin; + let n1 = axis == 'x' ? extent.xmax : extent.ymax; + if (hasValue(flipOrigin) && hasValue(flipOrigin![axis])) { + // without a flip origin, the flip point is halfway between the min and max values + let center = n1 - n0 + // with a set origin point, we need to calculate the distance from the min and max values + let flipN = flipOrigin![axis]; + if (flipN < center) { + n0 = n1 - (flipN * 2); + } else if (flipN > center) { + n1 = n0 + (flipN * 2); + } + } + if (axis == 'x') { + transformation.flipX(n0, n1); + } else { + transformation.flipY(n0, n1); + } + if (jsGeometries.length == 1) { + let jsFlip = affineTransformOperator.execute(jsGeometries[0] as any, transformation); + return buildDotNetGeometry(jsFlip); + } + let jsFlip = affineTransformOperator.executeMany(jsGeometries, transformation); + return jsFlip.map(g => buildDotNetGeometry(g) as DotNetGeometry); } - async generalize(geometry: DotNetGeometry, maxDeviation: number, removeDegenerateParts: boolean | null, - maxDeviationUnit: LinearUnits | null): Promise { + async generalize(geometries: DotNetGeometry | DotNetGeometry[], maxDeviation: number, + removeDegenerateParts: boolean | null, + maxDeviationUnit: LengthUnit | null): Promise { let generalizeOperator = await import('@arcgis/core/geometry/operators/generalizeOperator'); - let jsGeometry = buildJsGeometry(geometry) as Geometry; - let jsGeneralize: GeometryUnion; + + let jsGeneralize: GeometryUnion | GeometryUnion[]; let options: any = {}; + let hasOptions = false; if (hasValue(removeDegenerateParts)) { options.removeDegenerateParts = removeDegenerateParts; + hasOptions = true; } if (hasValue(maxDeviationUnit)) { options.unit = maxDeviationUnit; + hasOptions = true; + } + + if (geometries instanceof Array) { + let jsGeometries = geometries.map(buildJsGeometry) as GeometryUnion[]; + + if (hasOptions) { + jsGeneralize = generalizeOperator.executeMany(jsGeometries, maxDeviation, options) as GeometryUnion[]; + } else { + jsGeneralize = generalizeOperator.executeMany(jsGeometries, maxDeviation, options) as GeometryUnion[]; + } + + return jsGeneralize.map(g => buildDotNetGeometry(g) as DotNetGeometry); } - - jsGeneralize = generalizeOperator.execute(jsGeometry as GeometryUnion, maxDeviation, options) as GeometryUnion; - return this.returnToDotNet ? buildDotNetGeometry(jsGeneralize) : jsGeneralize; + let jsGeometry = buildJsGeometry(geometries) as GeometryUnion; + if (hasOptions) { + jsGeneralize = generalizeOperator.execute(jsGeometry as GeometryUnion, maxDeviation, options) as GeometryUnion; + } else { + jsGeneralize = generalizeOperator.execute(jsGeometry as GeometryUnion, maxDeviation) as GeometryUnion; + } + + return buildDotNetGeometry(jsGeneralize); } - async geodesicArea(geometry: DotNetPolygon, unit: AreaUnits | null): Promise { + async geodesicArea(geometry: DotNetPolygon, unit: AreaUnit | null): Promise { let geodeticAreaOperator = await import('@arcgis/core/geometry/operators/geodeticAreaOperator'); if (!geodeticAreaOperator.isLoaded()) { await geodeticAreaOperator.load(); @@ -256,7 +286,7 @@ export default class GeometryEngineWrapper { } async geodesicBuffer(geometries: Array | DotNetGeometry, distances: Array | number, - unit: LinearUnits | null, unionResults: boolean | null): Promise { + unit: LengthUnit | null, unionResults: boolean | null): Promise { let geodesicBufferOperator = await import('@arcgis/core/geometry/operators/geodesicBufferOperator'); if (!geodesicBufferOperator.isLoaded()) { await geodesicBufferOperator.load(); @@ -276,28 +306,50 @@ export default class GeometryEngineWrapper { jsGeometries = []; geometries.forEach(g => (jsGeometries as Array).push(buildJsGeometry(g) as GeometryUnion)); jsBuffer = geodesicBufferOperator.executeMany(jsGeometries as GeometryUnion[], distances as number[], options); - return this.returnToDotNet ? jsBuffer.map(g => buildDotNetPolygon(g) as DotNetPolygon) : jsBuffer; + return jsBuffer.map(g => buildDotNetPolygon(g) as DotNetPolygon); } jsGeometries = buildJsGeometry(geometries) as Geometry; jsBuffer = geodesicBufferOperator.execute(jsGeometries as GeometryUnion, distances as number, options) as Polygon; - return this.returnToDotNet ? buildDotNetPolygon(jsBuffer) : jsBuffer; + return buildDotNetPolygon(jsBuffer); } - async geodesicDensify(geometry: DotNetGeometry, maxSegmentLength: number, - maxSegmentLengthUnit: LinearUnits | null): Promise { + async geodesicDensify(geometries: DotNetGeometry | DotNetGeometry[], maxSegmentLength: number, + maxSegmentLengthUnit: LengthUnit | null): Promise { let geodeticDensifyOperator = await import('@arcgis/core/geometry/operators/geodeticDensifyOperator'); if (!geodeticDensifyOperator.isLoaded()) { await geodeticDensifyOperator.load(); } - let jsDensify: GeometryUnion; + let options: any = {}; + let hasOptions = false; if (hasValue(maxSegmentLengthUnit)) { options.unit = maxSegmentLengthUnit; + hasOptions = true; + } + + let jsDensify: GeometryUnion | GeometryUnion[]; + + if (geometries instanceof Array) { + let jsGeometries = geometries.map(buildJsGeometry) as GeometryUnion[]; + if (hasOptions) { + jsDensify = geodeticDensifyOperator.executeMany(jsGeometries, maxSegmentLength, options) as GeometryUnion[]; + } else { + jsDensify = geodeticDensifyOperator.executeMany(jsGeometries, maxSegmentLength) as GeometryUnion[]; + } + + return jsDensify.map(buildDotNetGeometry); + } + + let jsGeometry = buildJsGeometry(geometries) as GeometryUnion; + + if (hasOptions) { + jsDensify = geodeticDensifyOperator.execute(jsGeometry, maxSegmentLength, options) as GeometryUnion; + } else { + jsDensify = geodeticDensifyOperator.execute(jsGeometry, maxSegmentLength) as GeometryUnion; } - jsDensify = geodeticDensifyOperator.execute(buildJsGeometry(geometry) as GeometryUnion, maxSegmentLength, options) as GeometryUnion; - return this.returnToDotNet ? buildDotNetGeometry(jsDensify) : jsDensify; + return buildDotNetGeometry(jsDensify); } @@ -322,12 +374,12 @@ export default class GeometryEngineWrapper { jsGeometries = []; geometry1.forEach(g => (jsGeometries as Array).push(buildJsGeometry(g) as GeometryUnion)); jsIntersection = intersectionOperator.executeMany(jsGeometries as GeometryUnion[], buildJsGeometry(geometry2) as GeometryUnion); - return this.returnToDotNet ? jsIntersection.map(g => buildDotNetGeometry(g) as DotNetGeometry) : jsIntersection; + return jsIntersection.map(g => buildDotNetGeometry(g) as DotNetGeometry); } jsGeometries = buildJsGeometry(geometry1) as GeometryUnion; jsIntersection = intersectionOperator.execute(jsGeometries, buildJsGeometry(geometry2) as GeometryUnion) as GeometryUnion; - return this.returnToDotNet ? buildDotNetGeometry(jsIntersection) : jsIntersection; + return buildDotNetGeometry(jsIntersection); } @@ -347,24 +399,24 @@ export default class GeometryEngineWrapper { : Promise { let proximityOperator = await import('@arcgis/core/geometry/operators/proximityOperator'); let jsResult = proximityOperator.getNearestCoordinate(buildJsGeometry(geometry) as any, buildJsPoint(inputPoint) as Point); - return this.returnToDotNet ? { + return { coordinate: buildJsPoint(jsResult.coordinate), distance: jsResult.distance, isEmpty: jsResult.isEmpty, vertexIndex: jsResult.vertexIndex, isRightSide: jsResult.isRightSide - } : jsResult; + }; } async nearestVertex(geometry: DotNetGeometry, inputPoint: DotNetPoint): Promise { let proximityOperator = await import('@arcgis/core/geometry/operators/proximityOperator'); let jsResult = proximityOperator.getNearestVertex(buildJsGeometry(geometry) as GeometryUnion, buildJsPoint(inputPoint) as Point); - return this.returnToDotNet ? { + return { coordinate: buildDotNetPoint(jsResult.coordinate) as DotNetPoint, distance: jsResult.distance, vertexIndex: jsResult.vertexIndex, isEmpty: jsResult.isEmpty - } : jsResult; + }; } @@ -374,51 +426,69 @@ export default class GeometryEngineWrapper { let jsResult = proximityOperator.getNearestVertices( buildJsGeometry(geometry) as GeometryUnion, buildJsPoint(inputPoint) as Point, searchRadius, maxVertexCountToReturn); - return this.returnToDotNet ? jsResult.filter(v => v !== undefined).map(r => { + return jsResult.filter(v => v !== undefined).map(r => { return { coordinate: buildDotNetPoint(r.coordinate) as DotNetPoint, distance: r.distance, vertexIndex: r.vertexIndex, isEmpty: r.isEmpty }; - }) : jsResult.filter(v => v !== undefined); + }); } async offset(geometries: Array | DotNetGeometry, offsetDistance: number, - offsetUnit: LinearUnits | null | undefined, joinType: any | null | undefined, - bevelRatio: number | null | undefined, flattenError: number | null | undefined): Promise { + offsetUnit: LengthUnit | null | undefined, joinType: any | null | undefined, + miterLimit: number | null | undefined, flattenError: number | null | undefined): Promise { let offsetOperator = await import('@arcgis/core/geometry/operators/offsetOperator'); let jsGeometries: GeometryUnion | Array let options: any = {}; + let hasOptions = false; if (hasValue(offsetUnit)) { options.unit = offsetUnit; + hasOptions = true; } if (hasValue(joinType)) { options.joins = joinType; + hasOptions = true; } if (hasValue(flattenError)) { options.flattenError = flattenError; + hasOptions = true; + } + + if (hasValue(miterLimit)) { + options.miterLimit = miterLimit; + hasOptions = true; } + + let jsOffset: GeometryUnion | Array; if (Array.isArray(geometries)) { jsGeometries = []; geometries.forEach(g => (jsGeometries as Array).push(buildJsGeometry(g) as GeometryUnion)); - let jsOffset = offsetOperator.executeMany(jsGeometries as GeometryUnion[], offsetDistance, options); - return this.returnToDotNet ? jsOffset.map(g => buildDotNetGeometry(g) as DotNetGeometry) : jsOffset; + if (hasOptions) { + jsOffset = offsetOperator.executeMany(jsGeometries as GeometryUnion[], offsetDistance, options); + } else { + jsOffset = offsetOperator.executeMany(jsGeometries as GeometryUnion[], offsetDistance); + } + return jsOffset.map(g => buildDotNetGeometry(g) as DotNetGeometry); } jsGeometries = buildJsGeometry(geometries as DotNetGeometry) as GeometryUnion; - let jsOffset = offsetOperator.execute(jsGeometries as GeometryUnion, offsetDistance, options) as GeometryUnion; - return this.returnToDotNet ? buildDotNetGeometry(jsOffset) : jsOffset; + if (hasOptions) { + jsOffset = offsetOperator.execute(jsGeometries as GeometryUnion, offsetDistance, options) as GeometryUnion; + } else { + jsOffset = offsetOperator.execute(jsGeometries as GeometryUnion, offsetDistance) as GeometryUnion; + } + return buildDotNetGeometry(jsOffset); } async overlaps(geometry1: DotNetGeometry, geometry2: DotNetGeometry): Promise { let overlapsOperator = await import('@arcgis/core/geometry/operators/overlapsOperator'); return overlapsOperator.execute(buildJsGeometry(geometry1) as GeometryUnion, buildJsGeometry(geometry2) as GeometryUnion); - } - async planarArea(geometry: DotNetPolygon, unit: AreaUnits | null): Promise { + async planarArea(geometry: DotNetPolygon, unit: AreaUnit | null): Promise { let areaOperator = await import('@arcgis/core/geometry/operators/areaOperator'); if (unit === null) { return areaOperator.execute(buildJsPolygon(geometry) as Polygon); @@ -426,7 +496,7 @@ export default class GeometryEngineWrapper { return areaOperator.execute(buildJsPolygon(geometry) as Polygon, {unit: unit as any}); } - async planarLength(geometry: DotNetGeometry, unit: LinearUnits | null): Promise { + async planarLength(geometry: DotNetGeometry, unit: LengthUnit | null): Promise { let lengthOperator = await import('@arcgis/core/geometry/operators/lengthOperator'); if (unit === null) { return lengthOperator.execute(buildJsGeometry(geometry) as GeometryUnion); @@ -441,23 +511,55 @@ export default class GeometryEngineWrapper { } - async rotate(geometry: DotNetGeometry, angle: number, rotationOrigin: DotNetPoint): Promise { + async rotate(geometries: DotNetGeometry | DotNetGeometry[], angle: number, rotationOrigin: DotNetPoint): Promise { let { default: Transformation } = await import('@arcgis/core/geometry/operators/support/Transformation'); let transformation = new Transformation(); let affineTransformOperator = await import('@arcgis/core/geometry/operators/affineTransformOperator'); - let jsRotationOrigin = buildJsPoint(rotationOrigin) as Point; + + let jsGeometries: Array = []; + if (geometries instanceof Array) { + jsGeometries = geometries.map(buildJsGeometry) as Array; + } else { + jsGeometries.push(buildJsGeometry(geometries) as GeometryUnion); + } + + let jsRotationOrigin: Point; + + if (!hasValue(rotationOrigin)) { + let geom: GeometryUnion = jsGeometries[0] as GeometryUnion; + + if (jsGeometries.length > 1) { + let unionOperator = await import('@arcgis/core/geometry/operators/unionOperator'); + geom = unionOperator.executeMany(jsGeometries) as GeometryUnion; + } + let centroidOperator = await import('@arcgis/core/geometry/operators/centroidOperator'); + jsRotationOrigin = centroidOperator.execute(geom); + } else { + jsRotationOrigin = buildJsPoint(rotationOrigin) as Point; + } + transformation.rotate(angle, jsRotationOrigin.x, jsRotationOrigin.y) - let jsRotated = affineTransformOperator.execute(buildJsGeometry(geometry) as GeometryUnion, - transformation); - return this.returnToDotNet ? buildDotNetGeometry(jsRotated) : jsRotated; + + let jsRotated: GeometryUnion | Array; + if (jsGeometries.length > 1) { + jsRotated = affineTransformOperator.executeMany(jsGeometries, transformation) as Array; + return jsRotated.map(g => buildDotNetGeometry(g) as DotNetGeometry); + } + jsRotated = affineTransformOperator.execute(jsGeometries[0] as GeometryUnion, transformation); + return buildDotNetGeometry(jsRotated); } - async simplify(geometry: DotNetGeometry): Promise { + async simplify(geometries: DotNetGeometry | DotNetGeometry[]): Promise { let simplifyOperator = await import('@arcgis/core/geometry/operators/simplifyOperator'); - let jsSimplified = simplifyOperator.execute(buildJsGeometry(geometry) as GeometryUnion); - return this.returnToDotNet ? buildDotNetGeometry(jsSimplified) : jsSimplified; + if (geometries instanceof Array) { + let jsGeometries = geometries.map(buildJsGeometry) as Array; + let jsSimplified = simplifyOperator.executeMany(jsGeometries) as GeometryUnion[]; + return jsSimplified.map(g => buildDotNetGeometry(g) as DotNetGeometry); + } + let jsSimplified = simplifyOperator.execute(buildJsGeometry(geometries) as GeometryUnion); + return buildDotNetGeometry(jsSimplified); } async symmetricDifference(leftGeometry: Array | DotNetGeometry, rightGeometry: DotNetGeometry) @@ -468,12 +570,12 @@ export default class GeometryEngineWrapper { jsGeometries = []; leftGeometry.forEach(g => (jsGeometries as Array).push(buildJsGeometry(g) as GeometryUnion)); let jsDifference = symmetricDifferenceOperator.executeMany(jsGeometries as GeometryUnion[], buildJsGeometry(rightGeometry) as GeometryUnion); - return this.returnToDotNet ? jsDifference.map(g => buildDotNetGeometry(g) as DotNetGeometry) : jsDifference; + return jsDifference.map(g => buildDotNetGeometry(g) as DotNetGeometry); } jsGeometries = buildJsGeometry(leftGeometry) as GeometryUnion; let jsDifference = symmetricDifferenceOperator.execute(jsGeometries as GeometryUnion, buildJsGeometry(rightGeometry) as GeometryUnion); - return this.returnToDotNet ? buildDotNetGeometry(jsDifference) : jsDifference; + return buildDotNetGeometry(jsDifference); } @@ -493,7 +595,7 @@ export default class GeometryEngineWrapper { } let jsUnion = unionOperator.executeMany(jsGeometries); - return this.returnToDotNet ? buildDotNetGeometry(jsUnion) : jsUnion; + return buildDotNetGeometry(jsUnion); } async within(innerGeometry: DotNetGeometry, outerGeometry: DotNetGeometry): Promise { @@ -502,7 +604,7 @@ export default class GeometryEngineWrapper { buildJsGeometry(outerGeometry) as GeometryUnion); } - async fromJSON(json: string, typeName: string): Promise { + fromArcGisJson(json: string, typeName: string): any { let jsGeometry: Geometry; let jsonObject = JSON.parse(json); switch (typeName) { @@ -527,156 +629,127 @@ export default class GeometryEngineWrapper { default: throw new Error("Invalid geometry type"); } - return this.returnToDotNet ? buildDotNetGeometry(jsGeometry) : jsGeometry; + return buildDotNetGeometry(jsGeometry); } - async toJSON(geometry: any): Promise { + toArcGisJson(geometry: any): string { let jsGeometry = buildJsGeometry(geometry) as Geometry; return JSON.stringify(jsGeometry.toJSON()); } - async clone(geometry: DotNetGeometry): Promise { + clone(geometry: DotNetGeometry): any { let jsGeometry = buildJsGeometry(geometry) as Geometry; let clonedGeometry = jsGeometry.clone(); - return this.returnToDotNet ? buildDotNetGeometry(clonedGeometry) : clonedGeometry; + return buildDotNetGeometry(clonedGeometry); } - async centerExtentAt(extent: DotNetExtent, center: DotNetPoint): Promise { + centerExtentAt(extent: DotNetExtent, center: DotNetPoint): any { let jsExtent = buildJsExtent(extent, null) as Extent; let newExtent = jsExtent.centerAt(buildJsPoint(center) as Point); - return this.returnToDotNet ? buildDotNetExtent(newExtent) : newExtent; + return buildDotNetExtent(newExtent); } - async expand(extent: DotNetExtent, factor: number): Promise { + expand(extent: DotNetExtent, factor: number): any { let jsExtent = buildJsExtent(extent, null) as Extent; let newExtent = jsExtent.expand(factor); - return this.returnToDotNet ? buildDotNetExtent(newExtent) : newExtent; + return buildDotNetExtent(newExtent); } - async normalizeExtent(extent: DotNetExtent): Promise { + normalizeExtent(extent: DotNetExtent): DotNetExtent[] { let jsExtent = buildJsExtent(extent, null) as Extent; let newExtents = jsExtent.normalize(); return newExtents.map(e => buildDotNetExtent(e) as DotNetExtent); } - async offsetExtent(extent: DotNetExtent, dx: number, dy: number, dz: number): Promise { + offsetExtent(extent: DotNetExtent, dx: number, dy: number, dz: number): DotNetExtent { let jsExtent = buildJsExtent(extent, null) as Extent; let newExtent = jsExtent.offset(dx, dy, dz); return buildDotNetExtent(newExtent); } - async normalizePoint(point: DotNetPoint): Promise { + normalizePoint(point: DotNetPoint): DotNetPoint { let jsPoint = buildJsPoint(point) as Point; let newPoint = jsPoint.normalize(); return buildDotNetPoint(newPoint); } - async addPath(polyline: DotNetPolyline, path: any): Promise { + addPath(polyline: DotNetPolyline, path: any): DotNetPolyline { let jsPolyline = buildJsPolyline(polyline) as Polyline; - let newPolyline = jsPolyline.addPath(path); + let jsPaths = buildJsPathsOrRings([path]); + let newPolyline = jsPolyline.addPath(jsPaths[0]); return buildDotNetPolyline(newPolyline); } - async getPointOnPolyline(polyline: DotNetPolyline, pathIndex: number, pointIndex: number) - : Promise { - let jsPolyline = buildJsPolyline(polyline) as Polyline; - let jsPoint = jsPolyline.getPoint(pathIndex, pointIndex); + getPoint(geometry: DotNetGeometry, firstIndex: number, secondIndex: number) : DotNetPoint | null { + let jsGeometry = buildJsGeometry(geometry) as Geometry; + let jsPoint = (jsGeometry as any).getPoint(firstIndex, secondIndex); return buildDotNetPoint(jsPoint); } - async insertPointOnPolyline(polyline: DotNetPolyline, pathIndex: number, pointIndex: number, point: DotNetPoint) - : Promise { - let jsPolyline = buildJsPolyline(polyline) as Polyline; + insertPoint(geometry: DotNetGeometry, firstIndex: number, secondIndex: number, point: DotNetPoint) + : DotNetPolyline | null { + let jsGeometry = buildJsGeometry(geometry) as Geometry; let jsPoint = buildJsPoint(point) as Point; - let newPolyline = jsPolyline.insertPoint(pathIndex, pointIndex, jsPoint); - return buildDotNetPolyline(newPolyline); + let newPolyline = (jsGeometry as any).insertPoint(firstIndex, secondIndex, jsPoint); + return buildDotNetGeometry(newPolyline); } - async removePath(polyline: DotNetPolyline, pathIndex: number): Promise { + removePath(polyline: DotNetPolyline, pathIndex: number): any | null { let jsPolyline = buildJsPolyline(polyline) as Polyline; let path = jsPolyline.removePath(pathIndex); let newLine = buildDotNetPolyline(jsPolyline) as DotNetPolyline; - return { - polyLine: newLine, - path: path?.map(p => buildDotNetPoint(p) as DotNetPoint) - } + let result: DotNetGeometry[] = []; + result.push(newLine); + result.push(...path?.map(p => buildDotNetPoint(p) as DotNetPoint) ?? []); + return result; } - async removePointOnPolyline(polyline: DotNetPolyline, pathIndex: number, pointIndex: number): Promise { - let jsPolyline = buildJsPolyline(polyline) as Polyline; - let point = jsPolyline.removePoint(pathIndex, pointIndex); - return { - polyLine: buildDotNetPolyline(jsPolyline) as DotNetPolyline, - point: buildDotNetPoint(point) as DotNetPoint - }; + removePoint(geometry: DotNetGeometry, firstIndex: number, secondIndex: number): any | null { + let jsGeometry = buildJsGeometry(geometry) as Geometry; + let point = (jsGeometry as any).removePoint(firstIndex, secondIndex); + let result: DotNetGeometry[] = []; + result.push(buildDotNetGeometry(jsGeometry) as DotNetGeometry); + result.push(buildDotNetPoint(point) as DotNetPoint); + return result; } - async setPointOnPolyline(polyline: DotNetPolyline, pathIndex: number, pointIndex: number, point: DotNetPoint) - : Promise { - let jsPolyline = buildJsPolyline(polyline) as Polyline; + setPoint(geometry: DotNetGeometry, firstIndex: number, secondIndex: number, point: DotNetPoint) + : DotNetPolyline | null { + let jsGeometry = buildJsGeometry(geometry) as Geometry; let jsPoint = buildJsPoint(point) as Point; - let newPolyline = jsPolyline.setPoint(pathIndex, pointIndex, jsPoint); - return buildDotNetPolyline(newPolyline); + let newPolyline = (jsGeometry as any).setPoint(firstIndex, secondIndex, jsPoint); + return buildDotNetGeometry(newPolyline); } - async addRing(polygon: DotNetPolygon, ring: any): Promise { + addRing(polygon: DotNetPolygon, ring: any): DotNetPolygon { let jsPolygon = buildJsPolygon(polygon) as Polygon; - let newPolygon = jsPolygon.addRing(ring); + let jsRings = buildJsPathsOrRings([ring]); + let newPolygon = jsPolygon.addRing(jsRings[0]); return buildDotNetPolygon(newPolygon); } - async fromExtent(extent: DotNetExtent): Promise { + polygonFromExtent(extent: DotNetExtent): DotNetPolygon { let jsExtent = buildJsExtent(extent, null) as Extent; let jsPolygon = Polygon.fromExtent(jsExtent); return buildDotNetPolygon(jsPolygon); } - async getPointOnPolygon(polygon: DotNetPolygon, ringIndex: number, pointIndex: number): Promise { - let jsPolygon = buildJsPolygon(polygon) as Polygon; - let jsPoint = jsPolygon.getPoint(ringIndex, pointIndex); - return buildDotNetPoint(jsPoint); - } - async insertPointOnPolygon(polygon: DotNetPolygon, ringIndex: number, pointIndex: number, point: DotNetPoint) - : Promise { + isClockwise(polygon: DotNetPolygon, ring: any): boolean { let jsPolygon = buildJsPolygon(polygon) as Polygon; - let jsPoint = buildJsPoint(point) as Point; - let newPolygon = jsPolygon.insertPoint(ringIndex, pointIndex, jsPoint); - return buildDotNetPolygon(newPolygon); + let jsRings = buildJsPathsOrRings([ring]); + return jsPolygon.isClockwise(jsRings[0]); } - - async isClockwise(polygon: DotNetPolygon, ring: any): Promise { - let jsPolygon = buildJsPolygon(polygon) as Polygon; - return jsPolygon.isClockwise(ring); - } - - async removePointOnPolygon(polygon: DotNetPolygon, ringIndex: number, pointIndex: number): Promise { - let jsPolygon = buildJsPolygon(polygon) as Polygon; - let point = jsPolygon.removePoint(ringIndex, pointIndex); - return { - polygon: buildDotNetPolygon(jsPolygon) as DotNetPolygon, - point: buildDotNetPoint(point) as DotNetPoint - }; - } - - async removeRing(polygon: DotNetPolygon, index: number): Promise { + removeRing(polygon: DotNetPolygon, index: number): any { let jsPolygon = buildJsPolygon(polygon) as Polygon; let ring = jsPolygon.removeRing(index); - return { - polygon: buildDotNetPolygon(jsPolygon) as DotNetPolygon, - ring: ring?.map(p => buildDotNetPoint(p) as DotNetPoint) - }; - } - - async setPointOnPolygon(polygon: DotNetPolygon, ringIndex: number, pointIndex: number, point: DotNetPoint) - : Promise { - let jsPolygon = buildJsPolygon(polygon) as Polygon; - let jsPoint = buildJsPoint(point) as Point; - let newPolygon = jsPolygon.setPoint(ringIndex, pointIndex, jsPoint); - return buildDotNetPolygon(newPolygon); + let result: DotNetGeometry[] = []; + result.push(buildDotNetPolygon(jsPolygon) as DotNetPolygon); + result.push(...ring?.map(p => buildDotNetPoint(p) as DotNetPoint) ?? []); + return result; } getExtentCenter(extent: DotNetExtent): DotNetPoint { @@ -742,12 +815,21 @@ export default class GeometryEngineWrapper { return pointResults; } + + async convertMultipartGeometriesToSinglePartGeometries(geometries: DotNetGeometry[], simplifyPolygons: boolean) + : Promise { + const jsGeometries = geometries.map(buildJsGeometry); + let multiPartToSinglePartOperator = await import("@arcgis/core/geometry/operators/multiPartToSinglePartOperator"); + const results = multiPartToSinglePartOperator.executeMany(jsGeometries, + {simplifyPolygons: simplifyPolygons}); + return results.map(buildDotNetGeometry); + } } export async function buildJsGeometryEngine(dotNetObject: any, viewId: string | null): Promise { - return new GeometryEngineWrapper(dotNetObject); + // excluded } export async function buildDotNetGeometryEngine(jsObject: any, viewId: string | null): Promise { - return null; // not used + // excluded } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/graphic.ts b/src/dymaptic.GeoBlazor.Core/Scripts/graphic.ts index 49426e1eb..c263b32ec 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/graphic.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/graphic.ts @@ -110,7 +110,7 @@ export function buildDotNetGraphic(graphic: Graphic, layerId: string | null, vie dotNetGraphic.viewId = viewId; if (hasValue(graphic.symbol)) { - dotNetGraphic.symbol = buildDotNetSymbol(graphic.symbol, viewId); + dotNetGraphic.symbol = buildDotNetSymbol(graphic.symbol); } if (hasValue(graphic.geometry)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/label.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/label.gb.ts deleted file mode 100644 index 6c654686e..000000000 --- a/src/dymaptic.GeoBlazor.Core/Scripts/label.gb.ts +++ /dev/null @@ -1,256 +0,0 @@ -// File auto-generated by dymaptic tooling. Any changes made here will be lost on future generation. To override functionality, use the relevant root .ts file. -import LabelClass from '@arcgis/core/layers/support/LabelClass'; -import { arcGisObjectRefs, jsObjectRefs, dotNetRefs, hasValue, lookupGeoBlazorId, sanitize, removeCircularReferences, generateSerializableJson } from './geoBlazorCore'; -import BaseComponent from "./baseComponent"; - -export default class LabelGenerated extends BaseComponent { - public component: LabelClass; - public geoBlazorId: string | null = null; - public viewId: string | null = null; - public layerId: string | null = null; - - constructor(component: LabelClass) { - super(component); - this.component = component; - } - - - async updateComponent(dotNetObject: any): Promise { - if (hasValue(dotNetObject.symbol)) { - let { buildJsSymbol } = await import('./symbol'); - this.component.symbol = buildJsSymbol(dotNetObject.symbol, this.layerId, this.viewId) as any; - } - - if (hasValue(dotNetObject.allowOverrun)) { - this.component.allowOverrun = dotNetObject.allowOverrun; - } - if (hasValue(dotNetObject.deconflictionStrategy)) { - this.component.deconflictionStrategy = dotNetObject.deconflictionStrategy; - } - if (hasValue(dotNetObject.labelExpression)) { - this.component.labelExpression = dotNetObject.labelExpression; - } - if (hasValue(dotNetObject.labelExpressionInfo)) { - this.component.labelExpressionInfo = sanitize(dotNetObject.labelExpressionInfo); - } - if (hasValue(dotNetObject.labelPlacement)) { - this.component.labelPlacement = dotNetObject.labelPlacement; - } - if (hasValue(dotNetObject.labelPosition)) { - this.component.labelPosition = dotNetObject.labelPosition; - } - if (hasValue(dotNetObject.maxScale)) { - this.component.maxScale = dotNetObject.maxScale; - } - if (hasValue(dotNetObject.minScale)) { - this.component.minScale = dotNetObject.minScale; - } - if (hasValue(dotNetObject.repeatLabel)) { - this.component.repeatLabel = dotNetObject.repeatLabel; - } - if (hasValue(dotNetObject.repeatLabelDistance)) { - this.component.repeatLabelDistance = dotNetObject.repeatLabelDistance; - } - if (hasValue(dotNetObject.useCodedValues)) { - this.component.useCodedValues = dotNetObject.useCodedValues; - } - if (hasValue(dotNetObject.where)) { - this.component.where = dotNetObject.where; - } - } - - // region properties - - getLabelExpression(): any { - if (!hasValue(this.component.labelExpression)) { - return null; - } - - return generateSerializableJson(this.component.labelExpression); - } - - setLabelExpression(value: any): void { - this.component.labelExpression = JSON.parse(value); - } - - async getSymbol(): Promise { - if (!hasValue(this.component.symbol)) { - return null; - } - - let { buildDotNetSymbol } = await import('./symbol'); - return buildDotNetSymbol(this.component.symbol, this.viewId); - } - - async setSymbol(value: any): Promise { - let { buildJsSymbol } = await import('./symbol'); - this.component.symbol = buildJsSymbol(value, this.layerId, this.viewId); - } - - getWhere(): any { - if (!hasValue(this.component.where)) { - return null; - } - - return generateSerializableJson(this.component.where); - } - - setWhere(value: any): void { - this.component.where = JSON.parse(value); - } - -} - - -export async function buildJsLabelGenerated(dotNetObject: any, layerId: string | null, viewId: string | null): Promise { - if (!hasValue(dotNetObject)) { - return null; - } - - let properties: any = {}; - if (hasValue(dotNetObject.symbol)) { - let { buildJsSymbol } = await import('./symbol'); - properties.symbol = buildJsSymbol(dotNetObject.symbol, layerId, viewId) as any; - } - - if (hasValue(dotNetObject.allowOverrun)) { - properties.allowOverrun = dotNetObject.allowOverrun; - } - if (hasValue(dotNetObject.deconflictionStrategy)) { - properties.deconflictionStrategy = dotNetObject.deconflictionStrategy; - } - if (hasValue(dotNetObject.labelExpression)) { - properties.labelExpression = dotNetObject.labelExpression; - } - if (hasValue(dotNetObject.labelExpressionInfo)) { - properties.labelExpressionInfo = sanitize(dotNetObject.labelExpressionInfo); - } - if (hasValue(dotNetObject.labelPlacement)) { - properties.labelPlacement = dotNetObject.labelPlacement; - } - if (hasValue(dotNetObject.labelPosition)) { - properties.labelPosition = dotNetObject.labelPosition; - } - if (hasValue(dotNetObject.maxScale)) { - properties.maxScale = dotNetObject.maxScale; - } - if (hasValue(dotNetObject.minScale)) { - properties.minScale = dotNetObject.minScale; - } - if (hasValue(dotNetObject.repeatLabel)) { - properties.repeatLabel = dotNetObject.repeatLabel; - } - if (hasValue(dotNetObject.repeatLabelDistance)) { - properties.repeatLabelDistance = dotNetObject.repeatLabelDistance; - } - if (hasValue(dotNetObject.useCodedValues)) { - properties.useCodedValues = dotNetObject.useCodedValues; - } - if (hasValue(dotNetObject.where)) { - properties.where = dotNetObject.where; - } - let jsLabelClass = new LabelClass(properties); - - let { default: LabelWrapper } = await import('./label'); - - let labelWrapper = new LabelWrapper(jsLabelClass); - labelWrapper.geoBlazorId = dotNetObject.id; - labelWrapper.viewId = viewId; - labelWrapper.layerId = layerId; - - jsObjectRefs[dotNetObject.id] = labelWrapper; - arcGisObjectRefs[dotNetObject.id] = jsLabelClass; - - return jsLabelClass; -} - - -export async function buildDotNetLabelGenerated(jsObject: any, layerId: string | null, viewId: string | null): Promise { - if (!hasValue(jsObject)) { - return null; - } - - let dotNetLabel: any = {}; - - if (hasValue(jsObject.symbol)) { - let { buildDotNetSymbol } = await import('./symbol'); - dotNetLabel.symbol = buildDotNetSymbol(jsObject.symbol, viewId); - } - - if (hasValue(jsObject.allowOverrun)) { - dotNetLabel.allowOverrun = jsObject.allowOverrun; - } - - if (hasValue(jsObject.deconflictionStrategy)) { - dotNetLabel.deconflictionStrategy = removeCircularReferences(jsObject.deconflictionStrategy); - } - - if (hasValue(jsObject.labelExpression)) { - dotNetLabel.labelExpression = jsObject.labelExpression; - } - - if (hasValue(jsObject.labelExpressionInfo)) { - dotNetLabel.labelExpressionInfo = removeCircularReferences(jsObject.labelExpressionInfo); - } - - if (hasValue(jsObject.labelPlacement)) { - dotNetLabel.labelPlacement = removeCircularReferences(jsObject.labelPlacement); - } - - if (hasValue(jsObject.labelPosition)) { - dotNetLabel.labelPosition = removeCircularReferences(jsObject.labelPosition); - } - - if (hasValue(jsObject.maxScale)) { - dotNetLabel.maxScale = jsObject.maxScale; - } - - if (hasValue(jsObject.minScale)) { - dotNetLabel.minScale = jsObject.minScale; - } - - if (hasValue(jsObject.repeatLabel)) { - dotNetLabel.repeatLabel = jsObject.repeatLabel; - } - - if (hasValue(jsObject.repeatLabelDistance)) { - dotNetLabel.repeatLabelDistance = removeCircularReferences(jsObject.repeatLabelDistance); - } - - if (hasValue(jsObject.useCodedValues)) { - dotNetLabel.useCodedValues = jsObject.useCodedValues; - } - - if (hasValue(jsObject.where)) { - dotNetLabel.where = jsObject.where; - } - - - let geoBlazorId = lookupGeoBlazorId(jsObject); - if (hasValue(geoBlazorId)) { - dotNetLabel.id = geoBlazorId; - } else if (hasValue(viewId)) { - let dotNetRef = dotNetRefs[viewId!]; - if (hasValue(dotNetRef)) { - try { - dotNetLabel.id = await dotNetRef.invokeMethodAsync('GetId'); - } catch (e) { - console.error('Error invoking GetId for Label', e); - } - } - } - - if (hasValue(dotNetLabel.id)) { - if (!jsObjectRefs.hasOwnProperty(dotNetLabel.id)) { - let {default: LabelWrapper} = await import('./label'); - let labelWrapper = new LabelWrapper(jsObject); - labelWrapper.geoBlazorId = dotNetLabel.id; - labelWrapper.viewId = viewId; - labelWrapper.layerId = layerId; - jsObjectRefs[dotNetLabel.id] = labelWrapper; - } - arcGisObjectRefs[dotNetLabel.id] ??= jsObject; - } - return dotNetLabel; -} - diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/label.ts b/src/dymaptic.GeoBlazor.Core/Scripts/label.ts index f1499ce40..5807307f6 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/label.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/label.ts @@ -1,20 +1,137 @@ -import LabelGenerated from './label.gb'; -import LabelClass from '@arcgis/core/layers/support/LabelClass'; +import { + arcGisObjectRefs, + hasValue, + jsObjectRefs, + lookupGeoBlazorId, + removeCircularReferences, + sanitize +} from "./geoBlazorCore"; +import LabelClass from "@arcgis/core/layers/support/LabelClass"; + export async function buildJsLabel(dotNetObject: any, layerId: string | null, viewId: string | null): Promise { - let {buildJsLabelGenerated} = await import('./label.gb'); - return await buildJsLabelGenerated(dotNetObject, layerId, viewId); -} + if (!hasValue(dotNetObject)) { + return null; + } + + let properties: any = {}; + if (hasValue(dotNetObject.symbol)) { + let {buildJsSymbol} = await import('./symbol'); + properties.symbol = buildJsSymbol(dotNetObject.symbol, layerId, viewId) as any; + } + + if (hasValue(dotNetObject.allowOverrun)) { + properties.allowOverrun = dotNetObject.allowOverrun; + } + if (hasValue(dotNetObject.deconflictionStrategy)) { + properties.deconflictionStrategy = dotNetObject.deconflictionStrategy; + } + if (hasValue(dotNetObject.labelExpression)) { + properties.labelExpression = dotNetObject.labelExpression; + } + if (hasValue(dotNetObject.labelExpressionInfo)) { + properties.labelExpressionInfo = sanitize(dotNetObject.labelExpressionInfo); + } + if (hasValue(dotNetObject.labelPlacement)) { + properties.labelPlacement = dotNetObject.labelPlacement; + } + if (hasValue(dotNetObject.labelPosition)) { + properties.labelPosition = dotNetObject.labelPosition; + } + if (hasValue(dotNetObject.maxScale)) { + properties.maxScale = dotNetObject.maxScale; + } + if (hasValue(dotNetObject.minScale)) { + properties.minScale = dotNetObject.minScale; + } + if (hasValue(dotNetObject.repeatLabel)) { + properties.repeatLabel = dotNetObject.repeatLabel; + } + if (hasValue(dotNetObject.repeatLabelDistance)) { + properties.repeatLabelDistance = dotNetObject.repeatLabelDistance; + } + if (hasValue(dotNetObject.useCodedValues)) { + properties.useCodedValues = dotNetObject.useCodedValues; + } + if (hasValue(dotNetObject.where)) { + properties.where = dotNetObject.where; + } + let jsLabelClass = new LabelClass(properties); + + jsObjectRefs[dotNetObject.id] = jsLabelClass; + arcGisObjectRefs[dotNetObject.id] = jsLabelClass; -export async function buildDotNetLabel(jsObject: any, layerId: string | null, viewId: string | null): Promise { - let {buildDotNetLabelGenerated} = await import('./label.gb'); - return await buildDotNetLabelGenerated(jsObject, layerId, viewId); + return jsLabelClass; } -export default class LabelWrapper extends LabelGenerated { +export async function buildDotNetLabel(jsObject: any): Promise { + if (!hasValue(jsObject)) { + return null; + } - constructor(component: LabelClass) { - super(component); + let dotNetLabel: any = {}; + + if (hasValue(jsObject.symbol)) { + let {buildDotNetSymbol} = await import('./symbol'); + dotNetLabel.symbol = buildDotNetSymbol(jsObject.symbol); + } + + if (hasValue(jsObject.allowOverrun)) { + dotNetLabel.allowOverrun = jsObject.allowOverrun; + } + + if (hasValue(jsObject.deconflictionStrategy)) { + dotNetLabel.deconflictionStrategy = removeCircularReferences(jsObject.deconflictionStrategy); } - -} + if (hasValue(jsObject.labelExpression)) { + dotNetLabel.labelExpression = jsObject.labelExpression; + } + + if (hasValue(jsObject.labelExpressionInfo)) { + dotNetLabel.labelExpressionInfo = removeCircularReferences(jsObject.labelExpressionInfo); + } + + if (hasValue(jsObject.labelPlacement)) { + dotNetLabel.labelPlacement = removeCircularReferences(jsObject.labelPlacement); + } + + if (hasValue(jsObject.labelPosition)) { + dotNetLabel.labelPosition = removeCircularReferences(jsObject.labelPosition); + } + + if (hasValue(jsObject.maxScale)) { + dotNetLabel.maxScale = jsObject.maxScale; + } + + if (hasValue(jsObject.minScale)) { + dotNetLabel.minScale = jsObject.minScale; + } + + if (hasValue(jsObject.repeatLabel)) { + dotNetLabel.repeatLabel = jsObject.repeatLabel; + } + + if (hasValue(jsObject.repeatLabelDistance)) { + dotNetLabel.repeatLabelDistance = removeCircularReferences(jsObject.repeatLabelDistance); + } + + if (hasValue(jsObject.useCodedValues)) { + dotNetLabel.useCodedValues = jsObject.useCodedValues; + } + + if (hasValue(jsObject.where)) { + dotNetLabel.where = jsObject.where; + } + + let geoBlazorId = lookupGeoBlazorId(jsObject); + if (hasValue(geoBlazorId)) { + dotNetLabel.id = geoBlazorId; + } + + if (hasValue(dotNetLabel.id)) { + jsObjectRefs[dotNetLabel.id] ??= jsObject; + arcGisObjectRefs[dotNetLabel.id] ??= jsObject; + } + + return dotNetLabel; +} diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/layerSearchSource.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/layerSearchSource.gb.ts index e9af6bed2..a9d83a619 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/layerSearchSource.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/layerSearchSource.gb.ts @@ -21,7 +21,7 @@ export async function buildDotNetLayerSearchSourceGenerated(jsObject: any, viewI if (hasValue(jsObject.resultSymbol)) { let { buildDotNetSymbol } = await import('./symbol'); - dotNetLayerSearchSource.resultSymbol = buildDotNetSymbol(jsObject.resultSymbol, viewId); + dotNetLayerSearchSource.resultSymbol = buildDotNetSymbol(jsObject.resultSymbol); } if (hasValue(jsObject.autoNavigate)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/locationService.ts b/src/dymaptic.GeoBlazor.Core/Scripts/locationService.ts index 260b704a3..e0d6582dc 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/locationService.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/locationService.ts @@ -11,9 +11,10 @@ import locatorLocationToAddressParams = __esri.locatorLocationToAddressParams; import locatorAddressToLocationsParams = __esri.locatorAddressToLocationsParams; import locatorAddressesToLocationsParams = __esri.locatorAddressesToLocationsParams; import SuggestionResult = __esri.SuggestionResult; +import BaseComponent from "./baseComponent"; + +export default class LocatorWrapper extends BaseComponent { -export default class LocatorWrapper { - async addressesToLocations(url: string, addresses: any, countryCode: string | null, categories: string[] | null, locationType: string | null, outSpatialReference: DotNetSpatialReference | null, @@ -65,11 +66,7 @@ export default class LocatorWrapper { } let {buildDotNetAddressCandidate} = await import('./addressCandidate'); - let dotNetResult = result.map(r => buildDotNetAddressCandidate(r)); - - let json = JSON.stringify(dotNetResult); - let encoded = new TextEncoder().encode(json); - return encoded; + return result.map(r => buildDotNetAddressCandidate(r)); } async addressToLocations(url: string, address: any, categories: string[] | null, countryCode: string | null, @@ -133,11 +130,7 @@ export default class LocatorWrapper { } let {buildDotNetAddressCandidate} = await import('./addressCandidate'); - let dotNetResult = result.map(r => buildDotNetAddressCandidate(r)); - - let json = JSON.stringify(dotNetResult); - let encoded = new TextEncoder().encode(json); - return encoded; + return result.map(r => buildDotNetAddressCandidate(r)); } async locationToAddress(url: string, location: DotNetPoint, locationType: string | null, diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/locatorSearchSource.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/locatorSearchSource.gb.ts index 65d0eed14..fe4b61f2c 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/locatorSearchSource.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/locatorSearchSource.gb.ts @@ -118,7 +118,7 @@ export async function buildDotNetLocatorSearchSourceGenerated(jsObject: any, vie if (hasValue(jsObject.resultSymbol)) { let { buildDotNetSymbol } = await import('./symbol'); - dotNetLocatorSearchSource.resultSymbol = buildDotNetSymbol(jsObject.resultSymbol, viewId); + dotNetLocatorSearchSource.resultSymbol = buildDotNetSymbol(jsObject.resultSymbol); } if (hasValue(jsObject.apiKey)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/multipoint.ts b/src/dymaptic.GeoBlazor.Core/Scripts/multipoint.ts index 005f1349b..2396617ec 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/multipoint.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/multipoint.ts @@ -1,5 +1,5 @@ import MultipointGenerated from './multipoint.gb'; -import {hasValue} from './geoBlazorCore'; +import {hasValue, sanitize} from './geoBlazorCore'; import Multipoint from "@arcgis/core/geometry/Multipoint"; import {buildDotNetSpatialReference, buildJsSpatialReference} from "./spatialReference"; import {buildDotNetExtent} from "./extent"; @@ -15,7 +15,9 @@ export function buildJsMultipoint(dotNetObject: any): any { properties.hasZ = dotNetObject.hasZ; } if (hasValue(dotNetObject.points) && dotNetObject.points.length > 0) { - properties.points = dotNetObject.points; + properties.points = dotNetObject.points.map(p => { + return [p.x, p.y] + }); } if (hasValue(dotNetObject.spatialReference)) { properties.spatialReference = buildJsSpatialReference(dotNetObject.spatialReference); diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/opacityVariable.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/opacityVariable.gb.ts index 35859c2dd..ae8e01fa1 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/opacityVariable.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/opacityVariable.gb.ts @@ -2,7 +2,7 @@ import OpacityVariable from '@arcgis/core/renderers/visualVariables/OpacityVariable'; import { arcGisObjectRefs, jsObjectRefs, hasValue, removeCircularReferences } from './geoBlazorCore'; -export async function buildJsOpacityVariableGenerated(dotNetObject: any, layerId: string | null, viewId: string | null): Promise { +export async function buildJsOpacityVariableGenerated(dotNetObject: any): Promise { if (!hasValue(dotNetObject)) { return null; } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/opacityVariable.ts b/src/dymaptic.GeoBlazor.Core/Scripts/opacityVariable.ts index 51b840c02..e950d8c76 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/opacityVariable.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/opacityVariable.ts @@ -1,6 +1,6 @@ -export async function buildJsOpacityVariable(dotNetObject: any, layerId: string | null, viewId: string | null): Promise { +export async function buildJsOpacityVariable(dotNetObject: any): Promise { let { buildJsOpacityVariableGenerated } = await import('./opacityVariable.gb'); - return await buildJsOpacityVariableGenerated(dotNetObject, layerId, viewId); + return await buildJsOpacityVariableGenerated(dotNetObject); } export async function buildDotNetOpacityVariable(jsObject: any): Promise { let { buildDotNetOpacityVariableGenerated } = await import('./opacityVariable.gb'); diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/polygon.ts b/src/dymaptic.GeoBlazor.Core/Scripts/polygon.ts index 55d6d3b5c..413cd60da 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/polygon.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/polygon.ts @@ -2,7 +2,7 @@ import PolygonGenerated from './polygon.gb'; import {buildDotNetExtent} from "./extent"; import {buildDotNetSpatialReference, buildJsSpatialReference} from "./spatialReference"; import Polygon from "@arcgis/core/geometry/Polygon"; -import {arcGisObjectRefs, hasValue, jsObjectRefs} from './geoBlazorCore'; +import {arcGisObjectRefs, copyValuesIfExists, hasValue, jsObjectRefs} from './geoBlazorCore'; import Circle from "@arcgis/core/geometry/Circle"; import {buildDotNetPoint, buildJsPoint} from "./point"; import * as simplifyOperator from '@arcgis/core/geometry/operators/simplifyOperator'; @@ -50,7 +50,12 @@ export function buildDotNetPolygon(polygon: any): any { dnPolygon.geodesic = polygon.geodesic; } - dnPolygon.isSimple = simplifyOperator.isSimple(polygon); + try { + dnPolygon.isSimple = simplifyOperator.isSimple(polygon); + } catch (e) { + // invalid token + console.error(e); + } if (hasValue(polygon.isSelfIntersecting)) { dnPolygon.isSelfIntersecting = polygon.isSelfIntersecting; @@ -71,18 +76,16 @@ export function buildJsPolygon(dnPolygon: any): any { if (dnPolygon === undefined || dnPolygon === null) return null; let properties : any = {}; + if (hasValue(dnPolygon.rings)) { properties.rings = buildJsPathsOrRings(dnPolygon.rings); } + if (hasValue(dnPolygon.spatialReference)) { properties.spatialReference = buildJsSpatialReference(dnPolygon.spatialReference); } - if (hasValue(dnPolygon.hasM)) { - properties.hasM = dnPolygon.hasM; - } - if (hasValue(dnPolygon.hasZ)) { - properties.hasZ = dnPolygon.hasZ; - } + + copyValuesIfExists(dnPolygon, properties, 'hasM', 'hasZ'); let polygon: Polygon; if (hasValue(dnPolygon.center) && hasValue(dnPolygon.radius)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/polyline.ts b/src/dymaptic.GeoBlazor.Core/Scripts/polyline.ts index 5bdf83dd5..bb31c1743 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/polyline.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/polyline.ts @@ -2,37 +2,45 @@ import PolylineGenerated from './polyline.gb'; import {buildDotNetExtent} from "./extent"; import {buildDotNetSpatialReference, buildJsSpatialReference} from "./spatialReference"; import Polyline from "@arcgis/core/geometry/Polyline"; -import {arcGisObjectRefs, hasValue, jsObjectRefs} from './geoBlazorCore'; +import {arcGisObjectRefs, copyValuesIfExists, hasValue, jsObjectRefs} from './geoBlazorCore'; import * as simplifyOperator from '@arcgis/core/geometry/operators/simplifyOperator'; export function buildDotNetPolyline(polyline: any): any { - return { + let dnPolyline: any = { type: 'polyline', paths: polyline.paths, hasM: polyline.hasM, hasZ: polyline.hasZ, extent: buildDotNetExtent(polyline.extent), - spatialReference: buildDotNetSpatialReference(polyline.spatialReference), - isSimple: simplifyOperator.isSimple(polyline) + spatialReference: buildDotNetSpatialReference(polyline.spatialReference) }; + + try { + dnPolyline.isSimple = simplifyOperator.isSimple(polyline); + } catch (e) { + // invalid token + console.error(e); + } + + return dnPolyline; } export function buildJsPolyline(dnPolyline: any): any { if (dnPolyline === undefined || dnPolyline === null) return null; let properties: any = {}; + if (hasValue(dnPolyline.paths)) { properties.paths = buildJsPathsOrRings(dnPolyline.paths); } - if (hasValue(dnPolyline.hasZ)) { - properties.hasZ = dnPolyline.hasZ; - } - if (hasValue(dnPolyline.hasM)) { - properties.hasM = dnPolyline.hasM; - } + if (hasValue(dnPolyline.spatialReference)) { properties.spatialReference = buildJsSpatialReference(dnPolyline.spatialReference); } + + copyValuesIfExists(dnPolyline, properties, 'hasM', 'hasZ') + let polyline = new Polyline(properties); + let jsObjectRef = DotNet.createJSObjectReference(polyline); jsObjectRefs[dnPolyline.id] = jsObjectRef; arcGisObjectRefs[dnPolyline.id] = polyline; @@ -40,7 +48,7 @@ export function buildJsPolyline(dnPolyline: any): any { return polyline; } -function buildJsPathsOrRings(pathsOrRings: any) { +export function buildJsPathsOrRings(pathsOrRings: any) { if (!hasValue(pathsOrRings)) return null; if (pathsOrRings[0].hasOwnProperty("points")) { let array: [][][] = []; @@ -57,6 +65,26 @@ function buildJsPathsOrRings(pathsOrRings: any) { return pathsOrRings; } +export function buildProtobufPathsOrRings(pathsOrRings: any) { + if (!hasValue(pathsOrRings)) return null; + let array: any = []; + for (let i = 0; i < pathsOrRings.length; i++) { + let points = pathsOrRings[i]; + let ring : any = { + points: [] + } + + for (let j = 0; j < points.length; j++) { + ring.points.push({ + coordinates: points[j] + }) + } + array.push(ring); + } + + return array; +} + export default class PolylineWrapper extends PolylineGenerated { constructor(component: Polyline) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/portal.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/portal.gb.ts index 1c1bc2360..a05808500 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/portal.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/portal.gb.ts @@ -543,7 +543,7 @@ export default class PortalGenerated extends BaseComponent { } let { buildDotNetPortalFeaturedGroups } = await import('./portalFeaturedGroups'); - return await Promise.all(this.component.featuredGroups!.map(async i => await buildDotNetPortalFeaturedGroups(i, this.viewId))); + return await Promise.all(this.component.featuredGroups!.map(async i => await buildDotNetPortalFeaturedGroups(i))); } async setFeaturedGroups(value: any): Promise { @@ -692,7 +692,7 @@ export default class PortalGenerated extends BaseComponent { } let { buildDotNetPortalProperties } = await import('./portalProperties'); - return await buildDotNetPortalProperties(this.component.portalProperties, this.viewId); + return await buildDotNetPortalProperties(this.component.portalProperties); } async setPortalProperties(value: any): Promise { @@ -1074,12 +1074,12 @@ export async function buildDotNetPortalGenerated(jsObject: any, layerId: string if (hasValue(jsObject.featuredGroups)) { let { buildDotNetPortalFeaturedGroups } = await import('./portalFeaturedGroups'); - dotNetPortal.featuredGroups = await Promise.all(jsObject.featuredGroups.map(async i => await buildDotNetPortalFeaturedGroups(i, viewId))); + dotNetPortal.featuredGroups = await Promise.all(jsObject.featuredGroups.map(async i => await buildDotNetPortalFeaturedGroups(i))); } if (hasValue(jsObject.portalProperties)) { let { buildDotNetPortalProperties } = await import('./portalProperties'); - dotNetPortal.portalProperties = await buildDotNetPortalProperties(jsObject.portalProperties, viewId); + dotNetPortal.portalProperties = await buildDotNetPortalProperties(jsObject.portalProperties); } if (hasValue(jsObject.access)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/portal.ts b/src/dymaptic.GeoBlazor.Core/Scripts/portal.ts index 893e76b44..f3d425ccc 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/portal.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/portal.ts @@ -1,4 +1,3 @@ -import PortalGenerated from './portal.gb'; // override generated code in this file import PortalGenerated, {buildDotNetPortalGenerated, buildJsPortalGenerated} from './portal.gb'; import Portal from '@arcgis/core/portal/Portal'; diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/portalFeaturedGroups.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/portalFeaturedGroups.gb.ts index 5e4710d3e..88500befd 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/portalFeaturedGroups.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/portalFeaturedGroups.gb.ts @@ -22,7 +22,7 @@ export function buildJsPortalFeaturedGroupsGenerated(dotNetObject: any): any { } -export async function buildDotNetPortalFeaturedGroupsGenerated(jsObject: any, viewId: string | null): Promise { +export async function buildDotNetPortalFeaturedGroupsGenerated(jsObject: any): Promise { if (!hasValue(jsObject)) { return null; } @@ -36,26 +36,6 @@ export async function buildDotNetPortalFeaturedGroupsGenerated(jsObject: any, vi if (hasValue(jsObject.title)) { dotNetPortalFeaturedGroups.title = jsObject.title; } - - - let geoBlazorId = lookupGeoBlazorId(jsObject); - if (hasValue(geoBlazorId)) { - dotNetPortalFeaturedGroups.id = geoBlazorId; - } else if (hasValue(viewId)) { - let dotNetRef = dotNetRefs[viewId!]; - if (hasValue(dotNetRef)) { - try { - dotNetPortalFeaturedGroups.id = await dotNetRef.invokeMethodAsync('GetId'); - } catch (e) { - console.error('Error invoking GetId for PortalFeaturedGroups', e); - } - } - } - - if (hasValue(dotNetPortalFeaturedGroups.id)) { - jsObjectRefs[dotNetPortalFeaturedGroups.id] ??= jsObject; - arcGisObjectRefs[dotNetPortalFeaturedGroups.id] ??= jsObject; - } return dotNetPortalFeaturedGroups; } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/portalFeaturedGroups.ts b/src/dymaptic.GeoBlazor.Core/Scripts/portalFeaturedGroups.ts index 76aefed20..835c85f70 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/portalFeaturedGroups.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/portalFeaturedGroups.ts @@ -5,8 +5,8 @@ import { export function buildJsPortalFeaturedGroups(dotNetObject: any): Promise { return buildJsPortalFeaturedGroupsGenerated(dotNetObject); -} +} -export async function buildDotNetPortalFeaturedGroups(jsObject: any, viewId: string | null): Promise { - return await buildDotNetPortalFeaturedGroupsGenerated(jsObject, viewId); +export async function buildDotNetPortalFeaturedGroups(jsObject: any): Promise { + return await buildDotNetPortalFeaturedGroupsGenerated(jsObject); } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/portalProperties.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/portalProperties.gb.ts index 5344ba9fc..c420b5584 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/portalProperties.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/portalProperties.gb.ts @@ -217,7 +217,7 @@ export function buildJsPortalPropertiesGenerated(dotNetObject: any): any { } -export async function buildDotNetPortalPropertiesGenerated(jsObject: any, viewId: string | null): Promise { +export async function buildDotNetPortalPropertiesGenerated(jsObject: any): Promise { if (!hasValue(jsObject)) { return null; } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/portalProperties.ts b/src/dymaptic.GeoBlazor.Core/Scripts/portalProperties.ts index d6d0a8217..3c6189406 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/portalProperties.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/portalProperties.ts @@ -2,8 +2,8 @@ import {buildDotNetPortalPropertiesGenerated, buildJsPortalPropertiesGenerated} export function buildJsPortalProperties(dotNetObject: any): any { return buildJsPortalPropertiesGenerated(dotNetObject); -} +} -export async function buildDotNetPortalProperties(jsObject: any, viewId: string | null): Promise { - return await buildDotNetPortalPropertiesGenerated(jsObject, viewId); +export async function buildDotNetPortalProperties(jsObject: any): Promise { + return await buildDotNetPortalPropertiesGenerated(jsObject); } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/projection.ts b/src/dymaptic.GeoBlazor.Core/Scripts/projectionEngine.ts similarity index 88% rename from src/dymaptic.GeoBlazor.Core/Scripts/projection.ts rename to src/dymaptic.GeoBlazor.Core/Scripts/projectionEngine.ts index ae5e99a9b..7e2576337 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/projection.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/projectionEngine.ts @@ -4,14 +4,9 @@ import {buildDotNetGeometry, buildJsGeometry} from "./geometry"; import {buildJsSpatialReference} from "./spatialReference"; import {hasValue} from './geoBlazorCore'; import {buildJsExtent} from "./extent"; +import BaseComponent from "./baseComponent"; -export default class ProjectionWrapper { - private readonly returnToDotNet: boolean; - - constructor(returnToDotNet: boolean = true) { - this.returnToDotNet = returnToDotNet; - } - +export default class ProjectionWrapper extends BaseComponent { async load(): Promise { let projectionOperator = await import('@arcgis/core/geometry/operators/projectOperator'); if (!projectionOperator.isLoaded()) { @@ -35,9 +30,7 @@ export default class ProjectionWrapper { let jsGeometries = geometry.map(g => buildJsGeometry(g)); let result = projectionOperator.executeMany(jsGeometries, buildJsSpatialReference(outSpatialReference) as any, options); - if (!this.returnToDotNet) { - return result; - } + let resultArray: DotNetGeometry[] = []; (result as Geometry[]).forEach(g => { let dotNetGeom = buildDotNetGeometry(g); @@ -53,7 +46,7 @@ export default class ProjectionWrapper { let result = projectionOperator.execute(jsGeometry, buildJsSpatialReference(outSpatialReference) as any, options); - return this.returnToDotNet ? buildDotNetGeometry(result) : result; + return buildDotNetGeometry(result); } async getTransformation(inSpatialReference, outSpatialReference, extent): @@ -73,7 +66,7 @@ export default class ProjectionWrapper { buildJsSpatialReference(outSpatialReference) as any) } let {buildDotNetGeographicTransformation} = await import('./geographicTransformation'); - return this.returnToDotNet ? buildDotNetGeographicTransformation(geoTransform) : geoTransform; + return buildDotNetGeographicTransformation(geoTransform); } async getTransformations(inSpatialReference, outSpatialReference, extent): @@ -100,14 +93,14 @@ export default class ProjectionWrapper { dotNetTransforms.push(dotNetT); } }); - return this.returnToDotNet ? dotNetTransforms : geoTransforms; + return dotNetTransforms; } } export async function buildJsProjection(dotNetObject: any, layerId: string | null, viewId: string | null): Promise { - return new ProjectionWrapper(); + // not used } export async function buildDotNetProjection(jsObject: any, layerId: string | null, viewId: string | null): Promise { - return null; + // not used } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/searchViewModelDefaultSymbols.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/searchViewModelDefaultSymbols.gb.ts index a17dd5ee7..1182f3c17 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/searchViewModelDefaultSymbols.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/searchViewModelDefaultSymbols.gb.ts @@ -37,17 +37,17 @@ export async function buildDotNetSearchViewModelDefaultSymbolsGenerated(jsObject if (hasValue(jsObject.point)) { let { buildDotNetSymbol } = await import('./symbol'); - dotNetSearchViewModelDefaultSymbols.point = buildDotNetSymbol(jsObject.point, viewId); + dotNetSearchViewModelDefaultSymbols.point = buildDotNetSymbol(jsObject.point); } if (hasValue(jsObject.polygon)) { let { buildDotNetSymbol } = await import('./symbol'); - dotNetSearchViewModelDefaultSymbols.polygon = buildDotNetSymbol(jsObject.polygon, viewId); + dotNetSearchViewModelDefaultSymbols.polygon = buildDotNetSymbol(jsObject.polygon); } if (hasValue(jsObject.polyline)) { let { buildDotNetSymbol } = await import('./symbol'); - dotNetSearchViewModelDefaultSymbols.polyline = buildDotNetSymbol(jsObject.polyline, viewId); + dotNetSearchViewModelDefaultSymbols.polyline = buildDotNetSymbol(jsObject.polyline); } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/simpleRenderer.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/simpleRenderer.gb.ts index 5a6353808..3217ae654 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/simpleRenderer.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/simpleRenderer.gb.ts @@ -47,12 +47,12 @@ export async function buildDotNetSimpleRendererGenerated(jsObject: any, viewId: if (hasValue(jsObject.symbol)) { let { buildDotNetSymbol } = await import('./symbol'); - dotNetSimpleRenderer.symbol = buildDotNetSymbol(jsObject.symbol, viewId); + dotNetSimpleRenderer.symbol = buildDotNetSymbol(jsObject.symbol); } if (hasValue(jsObject.visualVariables)) { let { buildDotNetVisualVariable } = await import('./visualVariable'); - dotNetSimpleRenderer.visualVariables = await Promise.all(jsObject.visualVariables.map(async i => await buildDotNetVisualVariable(i, viewId))); + dotNetSimpleRenderer.visualVariables = await Promise.all(jsObject.visualVariables.map(async i => await buildDotNetVisualVariable(i))); } if (hasValue(jsObject.label)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/sizeRampStop.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/sizeRampStop.gb.ts index 060178dfa..c3687f94b 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/sizeRampStop.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/sizeRampStop.gb.ts @@ -44,7 +44,7 @@ export async function buildDotNetSizeRampStopGenerated(jsObject: any, viewId: st if (hasValue(jsObject.symbol)) { let { buildDotNetSymbol } = await import('./symbol'); - dotNetSizeRampStop.symbol = buildDotNetSymbol(jsObject.symbol, viewId); + dotNetSizeRampStop.symbol = buildDotNetSymbol(jsObject.symbol); } if (hasValue(jsObject.label)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/sizeVariable.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/sizeVariable.gb.ts index 4eb564846..ce9bf7fbc 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/sizeVariable.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/sizeVariable.gb.ts @@ -229,7 +229,7 @@ export async function buildJsSizeVariableGenerated(dotNetObject: any, layerId: s } -export async function buildDotNetSizeVariableGenerated(jsObject: any, layerId: string | null, viewId: string | null): Promise { +export async function buildDotNetSizeVariableGenerated(jsObject: any): Promise { if (!hasValue(jsObject)) { return null; } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/sizeVariable.ts b/src/dymaptic.GeoBlazor.Core/Scripts/sizeVariable.ts index e26fcf7f9..456a248d2 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/sizeVariable.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/sizeVariable.ts @@ -25,21 +25,7 @@ export async function buildJsSizeVariable(dotNetObject: any, layerId: string | n return await buildJsSizeVariableGenerated(dotNetObject, layerId, viewId); } -export async function buildDotNetSizeVariable(jsObject: any, layerId: string | null, viewId: string | null): Promise { +export async function buildDotNetSizeVariable(jsObject: any): Promise { let {buildDotNetSizeVariableGenerated} = await import('./sizeVariable.gb'); - let dotNetSizeVariable = await buildDotNetSizeVariableGenerated(jsObject, layerId, viewId); - - let geoBlazorId = lookupGeoBlazorId(jsObject); - if (hasValue(geoBlazorId)) { - dotNetSizeVariable.id = geoBlazorId; - } else if (hasValue(viewId)) { - let dotNetRef = dotNetRefs[viewId!]; - dotNetSizeVariable.id = await dotNetRef.invokeMethodAsync('GetId'); - } - if (hasValue(dotNetSizeVariable.id)) { - jsObjectRefs[dotNetSizeVariable.id] ??= jsObject; - arcGisObjectRefs[dotNetSizeVariable.id] ??= jsObject; - } - - return dotNetSizeVariable; + return await buildDotNetSizeVariableGenerated(jsObject); } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/sublayer.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/sublayer.gb.ts index 8e165870b..50749e7b4 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/sublayer.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/sublayer.gb.ts @@ -275,7 +275,7 @@ export default class SublayerGenerated extends BaseComponent { } let { buildDotNetLabel } = await import('./label'); - return await Promise.all(this.component.labelingInfo!.map(async i => await buildDotNetLabel(i, this.layerId, this.viewId))); + return await Promise.all(this.component.labelingInfo!.map(async i => await buildDotNetLabel(i))); } async setLabelingInfo(value: any): Promise { @@ -615,7 +615,7 @@ export async function buildDotNetSublayerGenerated(jsObject: any, layerId: strin if (hasValue(jsObject.labelingInfo)) { let { buildDotNetLabel } = await import('./label'); - dotNetSublayer.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i, layerId, viewId))); + dotNetSublayer.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i))); } if (hasValue(jsObject.orderBy)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/symbol.ts b/src/dymaptic.GeoBlazor.Core/Scripts/symbol.ts index efaf448af..890d7fec8 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/symbol.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/symbol.ts @@ -32,7 +32,7 @@ export function buildJsSymbol(symbol: any, layerId: string | null, viewId: strin } } -export function buildDotNetSymbol(symbol: any, viewId: string | null): any { +export function buildDotNetSymbol(symbol: any): any { if (!hasValue(symbol)) { return null; } @@ -50,7 +50,7 @@ export function buildDotNetSymbol(symbol: any, viewId: string | null): any { case 'text': return buildDotNetTextSymbol(symbol); case 'web-style': - return buildDotNetWebStyleSymbol(symbol, viewId); + return buildDotNetWebStyleSymbol(symbol); default: return removeCircularReferences(symbol); } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/symbolTableElementInfo.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/symbolTableElementInfo.gb.ts index 749ea1ab3..d2652bfb6 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/symbolTableElementInfo.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/symbolTableElementInfo.gb.ts @@ -41,7 +41,7 @@ export async function buildDotNetSymbolTableElementInfoGenerated(jsObject: any, if (hasValue(jsObject.symbol)) { let { buildDotNetSymbol } = await import('./symbol'); - dotNetSymbolTableElementInfo.symbol = buildDotNetSymbol(jsObject.symbol, viewId); + dotNetSymbolTableElementInfo.symbol = buildDotNetSymbol(jsObject.symbol); } if (hasValue(jsObject.label)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/topFeaturesQuery.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/topFeaturesQuery.gb.ts index 5fdaeaeae..d045e6983 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/topFeaturesQuery.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/topFeaturesQuery.gb.ts @@ -1,8 +1,8 @@ // File auto-generated by dymaptic tooling. Any changes made here will be lost on future generation. To override functionality, use the relevant root .ts file. import TopFeaturesQuery from '@arcgis/core/rest/support/TopFeaturesQuery'; -import { arcGisObjectRefs, jsObjectRefs, hasValue, removeCircularReferences } from './geoBlazorCore'; +import {arcGisObjectRefs, hasValue, jsObjectRefs, removeCircularReferences} from './geoBlazorCore'; -export async function buildJsTopFeaturesQueryGenerated(dotNetObject: any, layerId: string | null, viewId: string | null): Promise { +export async function buildJsTopFeaturesQueryGenerated(dotNetObject: any): Promise { if (!hasValue(dotNetObject)) { return null; } @@ -78,7 +78,7 @@ export async function buildJsTopFeaturesQueryGenerated(dotNetObject: any, layerI } -export async function buildDotNetTopFeaturesQueryGenerated(jsObject: any, layerId: string | null, viewId: string | null): Promise { +export async function buildDotNetTopFeaturesQueryGenerated(jsObject: any): Promise { if (!hasValue(jsObject)) { return null; } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/topFeaturesQuery.ts b/src/dymaptic.GeoBlazor.Core/Scripts/topFeaturesQuery.ts index 3d0e4c7ae..3e54ba388 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/topFeaturesQuery.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/topFeaturesQuery.ts @@ -1,11 +1,11 @@ // override generated code in this file -export async function buildJsTopFeaturesQuery(dotNetObject: any, layerId: string | null, viewId: string | null): Promise { +export async function buildJsTopFeaturesQuery(dotNetObject: any): Promise { let {buildJsTopFeaturesQueryGenerated} = await import('./topFeaturesQuery.gb'); - return await buildJsTopFeaturesQueryGenerated(dotNetObject, layerId, viewId); + return await buildJsTopFeaturesQueryGenerated(dotNetObject); } -export async function buildDotNetTopFeaturesQuery(jsObject: any, layerId: string | null, viewId: string | null): Promise { +export async function buildDotNetTopFeaturesQuery(jsObject: any): Promise { let {buildDotNetTopFeaturesQueryGenerated} = await import('./topFeaturesQuery.gb'); - return await buildDotNetTopFeaturesQueryGenerated(jsObject, layerId, viewId); + return await buildDotNetTopFeaturesQueryGenerated(jsObject); } diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/trackPartInfo.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/trackPartInfo.gb.ts index 4f1c0cb65..e6e52d2f3 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/trackPartInfo.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/trackPartInfo.gb.ts @@ -41,7 +41,7 @@ export async function buildDotNetTrackPartInfoGenerated(jsObject: any, layerId: if (hasValue(jsObject.labelingInfo)) { let { buildDotNetLabel } = await import('./label'); - dotNetTrackPartInfo.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i, layerId, viewId))); + dotNetTrackPartInfo.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i))); } if (hasValue(jsObject.renderer)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueClass.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueClass.gb.ts index c05609f41..5ce5b40ce 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueClass.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueClass.gb.ts @@ -38,7 +38,7 @@ export async function buildDotNetUniqueValueClassGenerated(jsObject: any, viewId if (hasValue(jsObject.symbol)) { let { buildDotNetSymbol } = await import('./symbol'); - dotNetUniqueValueClass.symbol = buildDotNetSymbol(jsObject.symbol, viewId); + dotNetUniqueValueClass.symbol = buildDotNetSymbol(jsObject.symbol); } if (hasValue(jsObject.values)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueInfo.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueInfo.gb.ts index 0dc57960c..e3631addc 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueInfo.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueInfo.gb.ts @@ -37,7 +37,7 @@ export async function buildDotNetUniqueValueInfoGenerated(jsObject: any, viewId: if (hasValue(jsObject.symbol)) { let { buildDotNetSymbol } = await import('./symbol'); - dotNetUniqueValueInfo.symbol = buildDotNetSymbol(jsObject.symbol, viewId); + dotNetUniqueValueInfo.symbol = buildDotNetSymbol(jsObject.symbol); } if (hasValue(jsObject.label)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueRenderer.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueRenderer.gb.ts index e180fa0e7..4ae7fdbf5 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueRenderer.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/uniqueValueRenderer.gb.ts @@ -130,7 +130,7 @@ export default class UniqueValueRendererGenerated extends BaseComponent { } let { buildDotNetSymbol } = await import('./symbol'); - return buildDotNetSymbol(this.component.defaultSymbol, this.viewId); + return buildDotNetSymbol(this.component.defaultSymbol); } async setDefaultSymbol(value: any): Promise { @@ -264,7 +264,7 @@ export default class UniqueValueRendererGenerated extends BaseComponent { } let { buildDotNetVisualVariable } = await import('./visualVariable'); - return await Promise.all(this.component.visualVariables!.map(async i => await buildDotNetVisualVariable(i, this.viewId))); + return await Promise.all(this.component.visualVariables!.map(async i => await buildDotNetVisualVariable(i))); } async setVisualVariables(value: any): Promise { @@ -372,7 +372,7 @@ export async function buildDotNetUniqueValueRendererGenerated(jsObject: any, lay if (hasValue(jsObject.defaultSymbol)) { let { buildDotNetSymbol } = await import('./symbol'); - dotNetUniqueValueRenderer.defaultSymbol = buildDotNetSymbol(jsObject.defaultSymbol, viewId); + dotNetUniqueValueRenderer.defaultSymbol = buildDotNetSymbol(jsObject.defaultSymbol); } if (hasValue(jsObject.legendOptions)) { @@ -392,7 +392,7 @@ export async function buildDotNetUniqueValueRendererGenerated(jsObject: any, lay if (hasValue(jsObject.visualVariables)) { let { buildDotNetVisualVariable } = await import('./visualVariable'); - dotNetUniqueValueRenderer.visualVariables = await Promise.all(jsObject.visualVariables.map(async i => await buildDotNetVisualVariable(i, viewId))); + dotNetUniqueValueRenderer.visualVariables = await Promise.all(jsObject.visualVariables.map(async i => await buildDotNetVisualVariable(i))); } if (hasValue(jsObject.defaultLabel)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/visualVariable.ts b/src/dymaptic.GeoBlazor.Core/Scripts/visualVariable.ts index 03ce8de80..0bf215f11 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/visualVariable.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/visualVariable.ts @@ -1,13 +1,13 @@ import VisualVariable from "@arcgis/core/renderers/visualVariables/VisualVariable"; -export async function buildDotNetVisualVariable(jsObject: any, viewId: string | null): Promise { +export async function buildDotNetVisualVariable(jsObject: any): Promise { switch (jsObject?.type) { case 'color': let {buildDotNetColorVariable} = await import('./colorVariable'); return await buildDotNetColorVariable(jsObject); case 'size': let {buildDotNetSizeVariable} = await import('./sizeVariable'); - return await buildDotNetSizeVariable(jsObject, viewId); + return await buildDotNetSizeVariable(jsObject); case 'opacity': let {buildDotNetOpacityVariable} = await import('./opacityVariable'); return await buildDotNetOpacityVariable(jsObject); diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/wFSLayer.gb.ts b/src/dymaptic.GeoBlazor.Core/Scripts/wFSLayer.gb.ts index 271e00244..e16b671f5 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/wFSLayer.gb.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/wFSLayer.gb.ts @@ -445,7 +445,7 @@ export default class WFSLayerGenerated extends BaseComponent { } let { buildDotNetLabel } = await import('./label'); - return await Promise.all(this.layer.labelingInfo!.map(async i => await buildDotNetLabel(i, this.layerId, this.viewId))); + return await Promise.all(this.layer.labelingInfo!.map(async i => await buildDotNetLabel(i))); } async setLabelingInfo(value: any): Promise { @@ -888,7 +888,7 @@ export async function buildDotNetWFSLayerGenerated(jsObject: any, layerId: strin if (hasValue(jsObject.labelingInfo)) { let { buildDotNetLabel } = await import('./label'); - dotNetWFSLayer.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i, layerId, viewId))); + dotNetWFSLayer.labelingInfo = await Promise.all(jsObject.labelingInfo.map(async i => await buildDotNetLabel(i))); } if (hasValue(jsObject.orderBy)) { diff --git a/src/dymaptic.GeoBlazor.Core/Scripts/webStyleSymbol.ts b/src/dymaptic.GeoBlazor.Core/Scripts/webStyleSymbol.ts index 80f9874ab..80c1f9f22 100644 --- a/src/dymaptic.GeoBlazor.Core/Scripts/webStyleSymbol.ts +++ b/src/dymaptic.GeoBlazor.Core/Scripts/webStyleSymbol.ts @@ -47,7 +47,7 @@ export function buildJsWebStyleSymbol(dotNetObject: any, layerId: string | null, return jsWebStyleSymbol; } -export function buildDotNetWebStyleSymbol(jsObject: any, viewId: string | null): any { +export function buildDotNetWebStyleSymbol(jsObject: any): any { if (!hasValue(jsObject)) { return null; } @@ -59,7 +59,7 @@ export function buildDotNetWebStyleSymbol(jsObject: any, viewId: string | null): } if (hasValue(jsObject.portal)) { - dotNetWebStyleSymbol.portal = buildDotNetPortal(jsObject.portal, viewId); + dotNetWebStyleSymbol.portal = buildDotNetPortal(jsObject.portal, null, null); } if (hasValue(jsObject.name)) { diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/ColorRampConverter.cs b/src/dymaptic.GeoBlazor.Core/Serialization/ColorRampConverter.cs index b218cac39..26620e697 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/ColorRampConverter.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/ColorRampConverter.cs @@ -32,10 +32,6 @@ internal class ColorRampConverter : JsonConverter public override void Write(Utf8JsonWriter writer, ColorRamp value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), newOptions)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/CoreSerializationData.cs b/src/dymaptic.GeoBlazor.Core/Serialization/CoreSerializationData.cs index 8651a13d3..8a175b538 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/CoreSerializationData.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/CoreSerializationData.cs @@ -13,4 +13,16 @@ internal static partial class CoreSerializationData public static partial Dictionary ProtoContractTypes { get; } public static partial Dictionary ProtoCollectionTypes { get; } + + public static partial object ToProtobufParameter(this object value, Type serializableType, bool isServer); + + public static partial object ToProtobufCollectionParameter(this IList items, Type serializableType, bool isServer); + + public static partial Task ReadJsStreamReferenceAsProtobuf( + this IJSStreamReference jsStreamReference, + Type returnType, long? maxAllowedSize = null, CancellationToken cancellationToken = default); + + public static partial Task ReadJsStreamReferenceAsProtobufCollection( + this IJSStreamReference jsStreamReference, + Type returnType, long? maxAllowedSize = null, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/GeoBlazorSerialization.cs b/src/dymaptic.GeoBlazor.Core/Serialization/GeoBlazorSerialization.cs index 8a9cebba3..e1f39a49a 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/GeoBlazorSerialization.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/GeoBlazorSerialization.cs @@ -19,6 +19,6 @@ static GeoBlazorSerialization() } }; } - + public static JsonSerializerOptions JsonSerializerOptions { get; private set; } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/GeometryConverter.cs b/src/dymaptic.GeoBlazor.Core/Serialization/GeometryConverter.cs index 6af3370b4..1823b7761 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/GeometryConverter.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/GeometryConverter.cs @@ -33,6 +33,7 @@ internal class GeometryConverter : JsonConverter // multipoint is in GeoBlazor Pro and must be loaded via Reflection Type? multipointType = Type.GetType("dymaptic.GeoBlazor.Pro.Components.Geometries.Multipoint, " + "dymaptic.GeoBlazor.Pro"); + if (multipointType is not null) { return (Geometry?)JsonSerializer.Deserialize(ref cloneReader, multipointType, newOptions); @@ -67,10 +68,6 @@ internal class GeometryConverter : JsonConverter public override void Write(Utf8JsonWriter writer, Geometry value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), newOptions)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/IJSStreamReferenceExtensions.cs b/src/dymaptic.GeoBlazor.Core/Serialization/IJSStreamReferenceExtensions.cs index ca23683f2..6d5393f46 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/IJSStreamReferenceExtensions.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/IJSStreamReferenceExtensions.cs @@ -6,18 +6,22 @@ namespace dymaptic.GeoBlazor.Core.Serialization; public static class IJSStreamReferenceExtensions { internal static async Task ReadJsStreamReferenceAsStream(this IJSStreamReference jsStreamReference, - long maxAllowedSize = 1_000_000_000L) + long? maxAllowedSize = null, CancellationToken cancellationToken = default) { - return await jsStreamReference.OpenReadStreamAsync(maxAllowedSize); + maxAllowedSize ??= 1_000_000_000L; + + return await jsStreamReference.OpenReadStreamAsync(maxAllowedSize.Value, cancellationToken); } /// /// Convenience method to deserialize an to a specific .NET type. /// internal static async Task ReadJsStreamReferenceAsJSON(this IJSStreamReference jsStreamReference, - long maxAllowedSize = 1_000_000_000L) + long? maxAllowedSize = null, CancellationToken cancellationToken = default) { - return (T?)await jsStreamReference.ReadJsStreamReferenceAsJSON(typeof(T), maxAllowedSize); + maxAllowedSize ??= 1_000_000_000L; + + return (T?)await jsStreamReference.ReadJsStreamReferenceAsJSON(typeof(T), maxAllowedSize, cancellationToken); } /// @@ -26,12 +30,16 @@ public static class IJSStreamReferenceExtensions /// internal static async Task ReadJsStreamReferenceAsJSON(this IJSStreamReference jsStreamReference, Type returnType, - long maxAllowedSize = 1_000_000_000) + long? maxAllowedSize = null, + CancellationToken cancellationToken = default) { - await using Stream stream = await jsStreamReference.OpenReadStreamAsync(maxAllowedSize); + maxAllowedSize ??= 1_000_000_000L; + + await using Stream stream = + await jsStreamReference.OpenReadStreamAsync(maxAllowedSize.Value, cancellationToken); using StreamReader reader = new(stream, Encoding.UTF8); - string json = await reader.ReadToEndAsync(); + string json = await reader.ReadToEndAsync(cancellationToken); if (returnType == typeof(string)) { diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/ImageryRendererConverter.cs b/src/dymaptic.GeoBlazor.Core/Serialization/ImageryRendererConverter.cs index 1014ae539..51631ba27 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/ImageryRendererConverter.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/ImageryRendererConverter.cs @@ -28,8 +28,9 @@ internal class ImageryRendererConverter : JsonConverter return null; default: // look for the type in GeoBlazor Pro - string typeName = - $"dymaptic.GeoBlazor.Pro.Components.Renderers.{typeValue.ToString()!.KebabToPascalCase()}Renderer"; + string typeName = + $"dymaptic.GeoBlazor.Pro.Components.Renderers.{typeValue.ToString()!.KebabToPascalCase() + }Renderer"; try { @@ -54,10 +55,6 @@ internal class ImageryRendererConverter : JsonConverter public override void Write(Utf8JsonWriter writer, IImageryRenderer value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), newOptions)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/JsSyncManager.cs b/src/dymaptic.GeoBlazor.Core/Serialization/JsSyncManager.cs index e275e41b0..c77d66386 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/JsSyncManager.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/JsSyncManager.cs @@ -24,12 +24,17 @@ public static class JsSyncManager /// public static Dictionary ProtoCollectionTypes { get; set; } = []; + /// + /// Collection of Reusable instances for components. + /// + public static Dictionary AbortManagers { get; set; } = []; + /// /// Initializes the JsSyncManager by registering protobuf types and compiling the runtime model. /// public static void Initialize() { - foreach (Type protoType in ProtoContractTypes.Values) + foreach (var protoType in ProtoContractTypes.Values) { RuntimeTypeModel.Default.Add(protoType, true); } @@ -37,7 +42,8 @@ public static void Initialize() RuntimeTypeModel.Default.CompileInPlace(); _serializableMethods = SerializableMethods - .ToDictionary(g => g.Key, g => g.Value.ToList()); + .ToDictionary(g => g.Key, + g => g.Value.ToList()); } /// @@ -65,8 +71,8 @@ public static async Task InvokeVoidJsMethod(this IJSObjectReference js, bool isS [CallerMemberName] string method = "", string className = "", CancellationToken cancellationToken = default, params object?[] parameters) { - SerializableMethodRecord methodRecord = GetMethodRecord(method, className, true, parameters); - List parameterList = GenerateSerializedParameters(methodRecord, parameters, isServer); + var methodRecord = GetMethodRecord(method, className, true, parameters); + var parameterList = await GenerateSerializedParameters(methodRecord, parameters, isServer, js); await js.InvokeVoidAsync("invokeVoidSerializedMethod", cancellationToken, [methodRecord.MethodName, isServer, ..parameterList]); @@ -87,6 +93,9 @@ await js.InvokeVoidAsync("invokeVoidSerializedMethod", cancellationToken, /// /// The name of the calling class. /// + /// + /// The maximum size of the returned data. + /// /// /// A CancellationToken to cancel an asynchronous operation. /// @@ -94,25 +103,24 @@ await js.InvokeVoidAsync("invokeVoidSerializedMethod", cancellationToken, /// The collection of parameters to pass to the JS call. /// public static async Task InvokeJsMethod(this IJSObjectReference js, bool isServer, - [CallerMemberName] string method = "", string className = "", + [CallerMemberName] string method = "", string className = "", long? maxAllowedSize = null, CancellationToken cancellationToken = default, params object?[] parameters) { - SerializableMethodRecord methodRecord = GetMethodRecord(method, className, false, parameters); + maxAllowedSize ??= 1_000_000_000L; + var methodRecord = GetMethodRecord(method, className, false, parameters); - List parameterList = GenerateSerializedParameters(methodRecord, parameters, isServer); + var parameterList = await GenerateSerializedParameters(methodRecord, parameters, isServer, js); + var returnType = methodRecord.ReturnValue?.Type; + var returnTypeIsProtobuf = returnType is not null && ProtoContractTypes.ContainsKey(returnType); - Type? returnType = methodRecord.ReturnValue?.Type; - bool returnTypeIsProtobuf = returnType is not null && ProtoContractTypes.ContainsKey(returnType); - - if (isServer || returnTypeIsProtobuf || returnType?.IsAssignableTo(typeof(Stream)) == true) + if (isServer || returnTypeIsProtobuf || (returnType?.IsAssignableTo(typeof(Stream)) == true)) { + Type? protoReturnType = null; string? protoReturnTypeName = null; if (returnTypeIsProtobuf) { - Type? protoReturnType; - if (methodRecord.ReturnValue!.SingleType is not null) { ProtoCollectionTypes.TryGetValue(methodRecord.ReturnValue.SingleType, @@ -126,40 +134,57 @@ public static async Task InvokeJsMethod(this IJSObjectReference js, bool i protoReturnTypeName = protoReturnType?.Name.Replace("SerializationRecord", ""); } - IJSStreamReference? streamRef = await js.InvokeAsync("invokeSerializedMethod", + var streamRef = await js.InvokeAsync("invokeSerializedMethod", cancellationToken, [methodRecord.MethodName, true, returnTypeIsProtobuf, protoReturnTypeName, ..parameterList]); if (streamRef is null) { - return default!; + return default(T)!; } if (returnTypeIsProtobuf) { if (methodRecord.ReturnValue?.SingleType is not null) { - return await streamRef.ReadJsStreamReferenceAsProtobufCollection(methodRecord.ReturnValue - .SingleType) ?? default!; + IProtobufSerializable[]? collectionResult = await streamRef + .ReadJsStreamReferenceAsProtobufCollection(methodRecord.ReturnValue.SingleType, + maxAllowedSize, cancellationToken); + + if (collectionResult is null) + { + return default(T)!; + } + + var typedArray = Array.CreateInstance(methodRecord.ReturnValue.SingleType, collectionResult.Length); + + Array.Copy(collectionResult, typedArray, collectionResult.Length); + + return (T)(object)typedArray; } - return await streamRef.ReadJsStreamReferenceAsProtobuf(returnType!) ?? default!; + var result = await streamRef.ReadJsStreamReferenceAsProtobuf(returnType!, + maxAllowedSize, cancellationToken); + + return result is null ? default(T)! : (T)result; } if (returnType?.IsAssignableTo(typeof(Stream)) == true) { - Stream? result = await streamRef.ReadJsStreamReferenceAsStream(); + // the calling code actually wants to handle the stream, so we will just unwrap and pass it back + var result = await streamRef.ReadJsStreamReferenceAsStream(maxAllowedSize, cancellationToken); if (result is null) { - return default!; + return default(T)!; } // double-cast to force to generic return (T)(object)result; } - return (await streamRef.ReadJsStreamReferenceAsJSON())!; + // read and deserialize the stream + return (await streamRef.ReadJsStreamReferenceAsJSON(maxAllowedSize, cancellationToken))!; } return await js.InvokeAsync("invokeSerializedMethod", cancellationToken, @@ -169,7 +194,7 @@ public static async Task InvokeJsMethod(this IJSObjectReference js, bool i private static SerializableMethodRecord GetMethodRecord(string method, string className, bool returnsVoid, object?[] providedParameters) { - if (!_serializableMethods.TryGetValue(className, out List? classMethods)) + if (!_serializableMethods.TryGetValue(className, out var classMethods)) { classMethods = new List(); _serializableMethods[className] = classMethods; @@ -181,7 +206,7 @@ private static SerializableMethodRecord GetMethodRecord(string method, string string.Equals(m.MethodName, method, StringComparison.OrdinalIgnoreCase) // same number of parameters - && m.Parameters.Length == providedParameters.Length + && (m.Parameters.Length == providedParameters.Length) // either both void or both non-void && ((m.ReturnValue is null && returnsVoid) @@ -194,88 +219,41 @@ private static SerializableMethodRecord GetMethodRecord(string method, string var methodInfos = classType.GetMethods(BindingFlags.Instance | BindingFlags.Public) .Where(m => string.Equals(m.Name, method, StringComparison.OrdinalIgnoreCase) - && m.GetParameters().Length == providedParameters.Length) + && (m.GetParameters().Length == providedParameters.Length)) .ToArray(); matchedMethods = []; - foreach (MethodInfo methodInfo in methodInfos) + foreach (var methodInfo in methodInfos.Where(m => m.ReturnType.IsGenericType)) { List methodParams = []; var paramInfos = methodInfo.GetParameters(); - foreach (ParameterInfo paramInfo in paramInfos) + foreach (var paramInfo in paramInfos) { - NullabilityInfo nullabilityInfo = nullabilityContext.Create(paramInfo); - bool isNullable = nullabilityInfo.ReadState == NullabilityState.Nullable; - - Type paramType = paramInfo.ParameterType.IsGenericType && - paramInfo.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>) - ? Nullable.GetUnderlyingType(paramInfo.ParameterType)! - : paramInfo.ParameterType; - - Type? collectionType = paramType.IsArray - ? paramType.GetElementType() - : paramType is { IsGenericType: true, GenericTypeArguments.Length: 1 } - ? paramType.GenericTypeArguments[0] - : null; - methodParams.Add(new SerializableParameterRecord(paramType, isNullable, collectionType)); + methodParams.Add(CreateParameterRecord(paramInfo.ParameterType, paramInfo)); } SerializableParameterRecord? returnRecord = null; - if (!returnsVoid && methodInfo.ReturnType != typeof(void)) + if (!returnsVoid && (methodInfo.ReturnType != typeof(void))) { - ParameterInfo returnParamInfo = methodInfo.ReturnParameter; - Type returnType = returnParamInfo.ParameterType; - - if (returnType.Name.StartsWith("Task") || returnType.Name.StartsWith("ValueTask")) - { - returnType = returnType.IsGenericType - ? returnType.GenericTypeArguments[0] - : typeof(void); - } - - bool isNullable = false; - - if (returnType.IsGenericType && - methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - isNullable = true; - returnType = Nullable.GetUnderlyingType(returnType)!; - } - - bool returnIsCollection = returnType.IsArray || - returnType is { IsGenericType: true, GenericTypeArguments.Length: 1 }; - - Type? singleType = null; - - if (returnIsCollection) - { - singleType = returnType.IsArray - ? returnType.GetElementType() - : returnType.GenericTypeArguments[0]; - } + var returnParamInfo = methodInfo.ReturnParameter; + var returnType = returnParamInfo.ParameterType; - bool isGenericParameter = returnType.IsGenericParameter; + returnRecord = CreateParameterRecord(returnType, returnParamInfo); - if (isGenericParameter) + if (returnRecord.Type.IsGenericType) { - returnType = returnType.GenericTypeArguments[0]; + // skip methods that return generic types, they cannot be handled + continue; } - - returnRecord = new SerializableParameterRecord(returnType, isNullable, singleType); } SerializableMethodRecord methodRecord = new(method.ToLowerFirstChar(), methodParams.ToArray(), returnRecord); matchedMethods.Add(methodRecord); - - if (methodRecord.ReturnValue?.Type.IsGenericType != true) - { - // only add non-generic return typed methods to the dictionary - classMethods.Add(methodRecord); - } + classMethods.Add(methodRecord); } } @@ -292,23 +270,23 @@ private static SerializableMethodRecord GetMethodRecord(string method, string return matchedMethods[0]; } - Type requestedReturnType = typeof(T); + var requestedReturnType = typeof(T); // find record with potentially matching parameter types including nulls return matchedMethods.First(m => { - for (int i = 0; i < m.Parameters.Length; i++) + for (var i = 0; i < m.Parameters.Length; i++) { - Type? providedParameterType = providedParameters[i]?.GetType(); + var providedParameterType = providedParameters[i]?.GetType(); if (providedParameterType is not null && providedParameterType.IsGenericType && - providedParameterType.GetGenericTypeDefinition() == typeof(Nullable<>)) + (providedParameterType.GetGenericTypeDefinition() == typeof(Nullable<>))) { providedParameterType = Nullable.GetUnderlyingType(providedParameterType)!; } - SerializableParameterRecord methodParam = m.Parameters[i]; + var methodParam = m.Parameters[i]; if (providedParameterType is null) { @@ -323,7 +301,7 @@ private static SerializableMethodRecord GetMethodRecord(string method, string } } - if (!returnsVoid && requestedReturnType != m.ReturnValue?.Type) + if (!returnsVoid && (requestedReturnType != m.ReturnValue?.Type)) { return false; } @@ -332,16 +310,16 @@ private static SerializableMethodRecord GetMethodRecord(string method, string }); } - private static List GenerateSerializedParameters(SerializableMethodRecord methodRecord, - object?[] parameters, bool isServer) + private static async Task> GenerateSerializedParameters(SerializableMethodRecord methodRecord, + object?[] parameters, bool isServer, IJSObjectReference js) { List serializedParameters = []; - for (int i = 0; i < parameters.Length; i++) + for (var i = 0; i < parameters.Length; i++) { - object? parameterValue = parameters[i]; - SerializableParameterRecord parameterRecord = methodRecord.Parameters[i]; - serializedParameters.AddRange(ProcessParameter(parameterValue, parameterRecord, isServer)); + var parameterValue = parameters[i]; + var parameterRecord = methodRecord.Parameters[i]; + serializedParameters.AddRange(await ProcessParameter(parameterValue, parameterRecord, isServer, js)); } return serializedParameters; @@ -359,15 +337,34 @@ private static SerializableMethodRecord GetMethodRecord(string method, string /// /// Boolean flag to identify if GeoBlazor is running in Blazor Server mode /// - private static object?[] ProcessParameter(object? parameterValue, SerializableParameterRecord parameterRecord, - bool isServer) + /// + /// The current class instance JS object reference + /// + private static async Task ProcessParameter(object? parameterValue, + SerializableParameterRecord parameterRecord, + bool isServer, IJSObjectReference js) { if (parameterValue is null) { return ["null", null]; } - Type paramType = parameterRecord.Type; + var paramType = parameterRecord.Type; + + if (paramType == typeof(CancellationToken)) + { + if (!AbortManagers.TryGetValue(js, out var abortManager)) + { + abortManager = new AbortManager(js); + AbortManagers[js] = abortManager; + } + + object? value = parameterValue is CancellationToken token + ? await abortManager.CreateAbortSignal(token) + : null; + + return ["abortSignal", value]; + } if (simpleTypes.Contains(paramType) || paramType.IsPrimitive) { @@ -382,31 +379,31 @@ private static SerializableMethodRecord GetMethodRecord(string method, string if (paramType.IsEnum) { // use the JsonConverter defined EnumToKebabCaseConverters to serialize enums as strings - string stringValue = JsonSerializer.Serialize(parameterValue, GeoBlazorSerialization.JsonSerializerOptions); + var stringValue = JsonSerializer.Serialize(parameterValue, GeoBlazorSerialization.JsonSerializerOptions); // pass as type string so JS can parse correctly return [nameof(String), stringValue]; } - if (paramType.IsGenericType && paramType.GetGenericTypeDefinition() == typeof(Nullable<>)) + if (paramType.IsGenericType && (paramType.GetGenericTypeDefinition() == typeof(Nullable<>))) { - Type underlyingType = Nullable.GetUnderlyingType(paramType)!; - object underlyingValue = Convert.ChangeType(parameterValue, underlyingType); + var underlyingType = Nullable.GetUnderlyingType(paramType)!; + var underlyingValue = Convert.ChangeType(parameterValue, underlyingType); - return ProcessParameter(underlyingValue, parameterRecord with { Type = underlyingType, IsNullable = true }, - isServer); + return await ProcessParameter(underlyingValue, + parameterRecord with { Type = underlyingType, IsNullable = true }, isServer, js); } - if (parameterValue is IList list && paramType != typeof(MapPath) && paramType != typeof(MapPoint)) + if (parameterValue is IList list && !geoblazorEnumerableTypes.Contains(paramType)) { - Type genericType = parameterRecord.SingleType ?? (paramType.IsArray + var genericType = parameterRecord.SingleType ?? (paramType.IsArray ? paramType.GetElementType()! : paramType.GenericTypeArguments[0]); - string key = $"{GetKey(genericType)}Collection"; + var key = $"{GetKey(genericType)}Collection"; if (ProtoContractTypes.ContainsKey(genericType)) { - object protobufParameter = list.ToProtobufCollectionParameter(genericType, isServer); + var protobufParameter = list.ToProtobufCollectionParameter(genericType, isServer); return [key, protobufParameter]; } @@ -416,14 +413,14 @@ private static SerializableMethodRecord GetMethodRecord(string method, string if (ProtoContractTypes.ContainsKey(paramType)) { - object protobufParameter = parameterValue.ToProtobufParameter(paramType, isServer); + var protobufParameter = parameterValue.ToProtobufParameter(paramType, isServer); return [GetKey(paramType), protobufParameter]; } if (parameterValue is AttributesDictionary attributesDictionary) { - AttributeSerializationRecord[] serializedItems = attributesDictionary.ToProtobufArray(); + var serializedItems = attributesDictionary.ToProtobufArray(); AttributeCollectionSerializationRecord collection = new(serializedItems); MemoryStream memoryStream = new(); Serializer.Serialize(memoryStream, collection); @@ -434,8 +431,8 @@ private static SerializableMethodRecord GetMethodRecord(string method, string return [nameof(AttributesDictionary), new DotNetStreamReference(memoryStream)]; } - byte[] data = memoryStream.ToArray(); - memoryStream.Dispose(); + var data = memoryStream.ToArray(); + await memoryStream.DisposeAsync(); return [nameof(AttributesDictionary), data]; } @@ -459,7 +456,7 @@ private static object ToJsonParameter(this T obj, bool isServer) private static string GetKey(Type type) { - Type? matchedType = ProtoContractTypes.Keys.FirstOrDefault(t => t == type); + var matchedType = ProtoContractTypes.Keys.FirstOrDefault(t => t == type); if (matchedType is not null) { @@ -473,63 +470,94 @@ private static SerializableParameterRecord GetSerializableParameterRecord(object { if (parameter is null) { - return new SerializableParameterRecord(typeof(object), true, null); + return new SerializableParameterRecord(typeof(object), true, + null, false); } - Type paramType = parameter.GetType(); + var paramType = parameter.GetType(); - if (simpleTypes.Contains(paramType)) - { - return new SerializableParameterRecord(paramType, true, null); - } + return CreateParameterRecord(paramType, null); + } - if (paramType.Name.Contains("AnonymousType")) + private static SerializableParameterRecord GetSerializableReturnRecord(bool returnsVoid) + { + if (returnsVoid) { - // anonymous object - return new SerializableParameterRecord(typeof(object), true, null); + return new SerializableParameterRecord(typeof(void), false, + null, false); } - bool isCollection = paramType.IsAssignableTo(typeof(IEnumerable)); + var returnType = typeof(T); - Type? collectionType = isCollection - ? paramType.IsArray - ? paramType.GetElementType() - : paramType.GetGenericArguments()[0] - : null; - - return new SerializableParameterRecord(paramType, true, collectionType); + return CreateParameterRecord(returnType, null); } - private static SerializableParameterRecord GetSerializableReturnRecord(bool returnsVoid) + private static SerializableParameterRecord CreateParameterRecord(Type paramType, ParameterInfo? parameterInfo) { - if (returnsVoid) + if (paramType.Name.StartsWith("Task") || paramType.Name.StartsWith("ValueTask")) { - return new SerializableParameterRecord(typeof(void), false, null); + paramType = paramType.IsGenericType + ? paramType.GenericTypeArguments[0] + : typeof(void); } - Type returnType = typeof(T); + if (simpleTypes.Contains(paramType) - if (simpleTypes.Contains(returnType)) + // these types will trigger the "isCollection" check below, but should be treated as single types + || geoblazorEnumerableTypes.Contains(paramType)) { - return new SerializableParameterRecord(returnType, true, null); + return new SerializableParameterRecord(paramType, true, + null, false); } - if (returnType == typeof(AttributesDictionary)) + if (paramType.Name.Contains("AnonymousType")) + { + // anonymous object + return new SerializableParameterRecord(typeof(object), true, + null, false); + } + + if (paramType == typeof(AttributesDictionary)) { return new SerializableParameterRecord(typeof(AttributesDictionary), true, - typeof(AttributeSerializationRecord)); + typeof(AttributeSerializationRecord), false); + } + + var typeIsNullable = IsTypeNullable(paramType) + || (parameterInfo is not null && IsParameterNullable(parameterInfo)); + + // unwrap nullable types + if (typeIsNullable && paramType.IsGenericType && (paramType.GetGenericTypeDefinition() == typeof(Nullable<>))) + { + paramType = Nullable.GetUnderlyingType(paramType)!; } - bool isCollection = returnType.IsAssignableTo(typeof(IEnumerable)) - && !returnType.IsAssignableTo(typeof(IDictionary)); + var isCollection = paramType.IsAssignableTo(typeof(IEnumerable)) + && !paramType.IsAssignableTo(typeof(IDictionary)); - Type? collectionType = isCollection - ? returnType.IsArray - ? returnType.GetElementType() - : returnType.GetGenericArguments()[0] + var collectionType = isCollection + ? paramType.IsArray + ? paramType.GetElementType() + : paramType.GenericTypeArguments[0] : null; - return new SerializableParameterRecord(returnType, true, collectionType); + var collectionTypeIsNullable = IsTypeNullable(collectionType); + + return new SerializableParameterRecord(paramType, typeIsNullable, + collectionType, collectionTypeIsNullable); + } + + private static bool IsParameterNullable(ParameterInfo paramInfo) + { + var nullabilityInfo = nullabilityContext.Create(paramInfo); + + return nullabilityInfo.ReadState == NullabilityState.Nullable; + } + + private static bool IsTypeNullable(Type? type) + { + return type is { IsGenericType: true } + && (type.GetGenericTypeDefinition() == typeof(Nullable<>)); } private static readonly NullabilityInfoContext nullabilityContext = new(); @@ -542,6 +570,11 @@ private static SerializableParameterRecord GetSerializableReturnRecord(bool r typeof(Guid), typeof(DateOnly), typeof(TimeOnly) ]; + private static readonly Type[] geoblazorEnumerableTypes = + [ + typeof(MapPath), typeof(MapPoint) + ]; + private static Dictionary> _serializableMethods = []; } @@ -562,4 +595,5 @@ public record SerializableMethodRecord( /// The type of the parameter. /// Whether the parameter is nullable. /// The single generic type argument, if applicable. -public record SerializableParameterRecord(Type Type, bool IsNullable, Type? SingleType); \ No newline at end of file +/// Whether the single generic type argument is nullable, if applicable. +public record SerializableParameterRecord(Type Type, bool IsNullable, Type? SingleType, bool SingleTypeIsNullable); \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/LayerConverter.cs b/src/dymaptic.GeoBlazor.Core/Serialization/LayerConverter.cs index 8b49be8f8..609fb0599 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/LayerConverter.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/LayerConverter.cs @@ -69,7 +69,7 @@ internal class LayerConverter : JsonConverter return null; default: // look for the type in GeoBlazor Pro - string typeName = + string typeName = $"dymaptic.GeoBlazor.Pro.Components.Layers.{typeValue.ToString()!.KebabToPascalCase()}Layer"; try @@ -95,6 +95,7 @@ internal class LayerConverter : JsonConverter temp.TryGetValue("arcGisLayerId", out object? arcGisLayerIdValue); string? arcGisLayerId = arcGisLayerIdValue?.ToString(); temp.TryGetValue("fullExtent", out object? fullExtentValue); + Extent? fullExtent = fullExtentValue is not null ? JsonSerializer.Deserialize(fullExtentValue.ToString()!, newOptions) : null; @@ -108,6 +109,7 @@ internal class LayerConverter : JsonConverter temp.TryGetValue("title", out object? titleValue); string? title = titleValue?.ToString(); temp.TryGetValue("visibilityTimeExtent", out object? visibilityTimeExtentValue); + TimeExtent? visibilityTimeExtent = visibilityTimeExtentValue is not null ? JsonSerializer.Deserialize(visibilityTimeExtentValue.ToString()!, newOptions) : null; @@ -120,11 +122,7 @@ internal class LayerConverter : JsonConverter public override void Write(Utf8JsonWriter writer, Layer value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), newOptions)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } } @@ -147,21 +145,21 @@ internal class FullExtentConverter : JsonConverter foreach (string propertyName in requiredProperties) { - if (!temp.ContainsKey(propertyName) + if (!temp.ContainsKey(propertyName) || temp[propertyName] is null or JsonElement { ValueKind: JsonValueKind.Null }) { return null; } } - + return JsonSerializer.Deserialize(ref cloneReader, newOptions); } public override void Write(Utf8JsonWriter writer, Extent value, JsonSerializerOptions options) { - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), options)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } - + private static readonly string[] requiredProperties = [ nameof(Extent.Xmax).ToLowerInvariant(), diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/MarkerSymbolJsonConverter.cs b/src/dymaptic.GeoBlazor.Core/Serialization/MarkerSymbolJsonConverter.cs index 85af99873..a069db2c1 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/MarkerSymbolJsonConverter.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/MarkerSymbolJsonConverter.cs @@ -34,10 +34,6 @@ internal class MarkerSymbolJsonConverter : JsonConverter public override void Write(Utf8JsonWriter writer, MarkerSymbol value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), newOptions)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/MeasurementWidgetActiveWidgetConverter.cs b/src/dymaptic.GeoBlazor.Core/Serialization/MeasurementWidgetActiveWidgetConverter.cs index c3b3223bf..c0c5c7d80 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/MeasurementWidgetActiveWidgetConverter.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/MeasurementWidgetActiveWidgetConverter.cs @@ -2,7 +2,8 @@ namespace dymaptic.GeoBlazor.Core.Serialization; internal class MeasurementWidgetActiveWidgetConverter : JsonConverter { - public override IMeasurementWidgetActiveWidget? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override IMeasurementWidgetActiveWidget? Read(ref Utf8JsonReader reader, Type typeToConvert, + JsonSerializerOptions options) { var newOptions = new JsonSerializerOptions(options) { @@ -28,7 +29,7 @@ internal class MeasurementWidgetActiveWidgetConverter : JsonConverter /// The type that this record can be converted to. public abstract record MapComponentSerializationRecord : MapComponentSerializationRecord + where T : IProtobufSerializable { /// /// Converts this serialization record back to the original type. diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/RendererConverter.cs b/src/dymaptic.GeoBlazor.Core/Serialization/RendererConverter.cs index 185717e43..c632b1dc6 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/RendererConverter.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/RendererConverter.cs @@ -28,7 +28,7 @@ internal class RendererConverter : JsonConverter return null; default: // look for the type in GeoBlazor Pro - string typeName = + string typeName = $"dymaptic.GeoBlazor.Pro.Components.Layers.{typeValue.ToString()!.KebabToPascalCase()}Renderer"; try @@ -54,10 +54,6 @@ internal class RendererConverter : JsonConverter public override void Write(Utf8JsonWriter writer, Renderer value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), newOptions)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/SizeVariableConverter.cs b/src/dymaptic.GeoBlazor.Core/Serialization/SizeVariableConverter.cs new file mode 100644 index 000000000..4031e22ae --- /dev/null +++ b/src/dymaptic.GeoBlazor.Core/Serialization/SizeVariableConverter.cs @@ -0,0 +1,217 @@ +namespace dymaptic.GeoBlazor.Core.Serialization; + +internal class SizeVariableConverter : JsonConverter +{ + public override SizeVariable? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // manually loop through the reader + // required because of the "MinSize" and "MaxSize" properties which can be either Dimension or SizeVariable + if (reader.TokenType == JsonTokenType.StartObject) + { + SizeVariable sizeVariable = new(); + string? currentPropertyName = null; +#pragma warning disable BL0005 + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonTokenType.PropertyName: + currentPropertyName = reader.GetString()?.ToUpperFirstChar(); + + break; + case JsonTokenType.EndObject: + return sizeVariable; + default: + switch (currentPropertyName) + { + case nameof(SizeVariable.Field): + sizeVariable.Field = reader.GetString(); + + break; + case nameof(SizeVariable.MinSize): + var (minDimension, minSizeVariable) = + GetDimensionOrSizeVariable(ref reader, options); + + if (minDimension is not null) + { + sizeVariable.MinSize = minDimension; + } + + if (minSizeVariable is not null) + { + sizeVariable.MinSize = minSizeVariable.MinSize; + } + + break; + case nameof(SizeVariable.MaxSize): + var (maxDimension, maxSizeVariable) = + GetDimensionOrSizeVariable(ref reader, options); + + if (maxDimension is not null) + { + sizeVariable.MaxSize = maxDimension; + } + + if (maxSizeVariable is not null) + { + sizeVariable.MaxSize = maxSizeVariable.MaxSize; + } + + break; + case nameof(SizeVariable.MinDataValue): + if (reader.TokenType == JsonTokenType.Number) + { + sizeVariable.MinDataValue = reader.GetDouble(); + } + + break; + case nameof(SizeVariable.MaxDataValue): + if (reader.TokenType == JsonTokenType.Number) + { + sizeVariable.MaxDataValue = reader.GetDouble(); + } + + break; + case nameof(SizeVariable.ValueRepresentation): + sizeVariable.ValueRepresentation = + JsonSerializer.Deserialize(ref reader, options); + + break; + case nameof(SizeVariable.ValueUnit): + sizeVariable.ValueUnit = + JsonSerializer.Deserialize(ref reader, options); + + break; + case nameof(SizeVariable.NormalizationField): + sizeVariable.NormalizationField = reader.GetString(); + + break; + case nameof(SizeVariable.Target): + sizeVariable.Target = reader.GetString(); + + break; + case nameof(SizeVariable.UseSymbolValue): + if (reader.TokenType != JsonTokenType.Null) + { + sizeVariable.UseSymbolValue = reader.GetBoolean(); + } + + break; + case nameof(SizeVariable.Axis): + sizeVariable.Axis = + JsonSerializer.Deserialize(ref reader, options); + + break; + case nameof(SizeVariable.ValueExpression): + sizeVariable.ValueExpression = reader.GetString(); + + break; + case nameof(SizeVariable.ValueExpressionTitle): + sizeVariable.ValueExpressionTitle = reader.GetString(); + + break; + case nameof(SizeVariable.LegendOptions): + sizeVariable.LegendOptions = JsonSerializer + .Deserialize(ref reader, options); + + break; + case nameof(SizeVariable.Stops): + sizeVariable.Stops = JsonSerializer + .Deserialize>(ref reader, options); + + break; + } + + break; + } + } + + return sizeVariable; + } +#pragma warning restore BL0005 + return null; + } + + public override void Write(Utf8JsonWriter writer, SizeVariable value, JsonSerializerOptions options) + { + PropertyInfo[] props = value.GetPropertyInfos() + .Where(p => p.SetMethod is not null) + .ToArray(); + + writer.WriteStartObject(); + + // type does not have a Getter, so it is not included in GetPropertyInfos + writer.WritePropertyName("type"); + writer.WriteStringValue("size"); + + foreach (PropertyInfo prop in props) + { + JsonIgnoreAttribute? ignoreAttribute = prop.GetCustomAttribute(); + + if (ignoreAttribute is not null && ignoreAttribute.Condition == JsonIgnoreCondition.WhenWritingNull) + { + continue; + } + + object? propValue = prop.GetValue(value); + + if (ignoreAttribute is not null && propValue is null + && ignoreAttribute.Condition == JsonIgnoreCondition.WhenWritingNull) + { + continue; + } + + if (prop.Name == nameof(SizeVariable.MinSizeVariable) || prop.Name == nameof(SizeVariable.MaxSizeVariable)) + { + writer.WritePropertyName(prop.Name.Replace("Variable", "").ToLowerFirstChar()); + } + else + { + writer.WritePropertyName(prop.Name.ToLowerFirstChar()); + } + + writer.WriteRawValue(JsonSerializer.Serialize(propValue, typeof(object), + GeoBlazorSerialization.JsonSerializerOptions)); + } + + writer.WriteEndObject(); + } + + private (Dimension? dimension, SizeVariable? sizeVariable) GetDimensionOrSizeVariable(ref Utf8JsonReader reader, + JsonSerializerOptions options) + { + Dimension? dimension = null; + SizeVariable? sizeVariable = null; + + switch (reader.TokenType) + { + case JsonTokenType.String: + var stringVal = reader.GetString(); + + if (stringVal is not null) + { + dimension = new Dimension(stringVal); + } + + break; + case JsonTokenType.Number: + var doubleVal = reader.GetDouble(); + dimension = new Dimension(doubleVal); + + break; + case JsonTokenType.StartObject: + try + { + sizeVariable = JsonSerializer.Deserialize(ref reader, options); + } + catch + { + dimension = JsonSerializer.Deserialize(ref reader, options); + } + + break; + } + + return (dimension, sizeVariable); + } +} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/SymbolJsonConverter.cs b/src/dymaptic.GeoBlazor.Core/Serialization/SymbolJsonConverter.cs index 7cd00b7f8..95c889719 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/SymbolJsonConverter.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/SymbolJsonConverter.cs @@ -38,7 +38,7 @@ internal class SymbolJsonConverter : JsonConverter return null; default: // look for the type in GeoBlazor Pro - string typeName = + string typeName = $"dymaptic.GeoBlazor.Pro.Components.Symbols.{typeValue.ToString()!.KebabToPascalCase()}Symbol"; try @@ -64,10 +64,6 @@ internal class SymbolJsonConverter : JsonConverter public override void Write(Utf8JsonWriter writer, Symbol value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), newOptions)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/VisualVariableConverter.cs b/src/dymaptic.GeoBlazor.Core/Serialization/VisualVariableConverter.cs index c51105945..428a7ccdb 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/VisualVariableConverter.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/VisualVariableConverter.cs @@ -4,28 +4,27 @@ internal class VisualVariableConverter : JsonConverter { public override VisualVariable? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - Utf8JsonReader cloneReader = reader; + var cloneReader = reader; - if (JsonSerializer.Deserialize>(ref reader, newOptions) is not - IDictionary temp) + if (JsonSerializer.Deserialize>(ref reader, + GeoBlazorSerialization.JsonSerializerOptions) is not IDictionary temp) { return null; } - if (temp.TryGetValue("type", out object? typeValue)) + if (temp.TryGetValue("type", out var typeValue)) { switch (typeValue?.ToString()) { case "size": - return JsonSerializer.Deserialize(ref cloneReader, newOptions); + return JsonSerializer.Deserialize(ref cloneReader, + GeoBlazorSerialization.JsonSerializerOptions); case "color": - return JsonSerializer.Deserialize(ref cloneReader, newOptions); + return JsonSerializer.Deserialize(ref cloneReader, + GeoBlazorSerialization.JsonSerializerOptions); case "opacity": - return JsonSerializer.Deserialize(ref cloneReader, newOptions); + return JsonSerializer.Deserialize(ref cloneReader, + GeoBlazorSerialization.JsonSerializerOptions); case null: return null; } @@ -36,10 +35,6 @@ internal class VisualVariableConverter : JsonConverter public override void Write(Utf8JsonWriter writer, VisualVariable value, JsonSerializerOptions options) { - var newOptions = new JsonSerializerOptions(options) - { - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), newOptions)); + JsonSerializer.Serialize(writer, value, typeof(object), GeoBlazorSerialization.JsonSerializerOptions); } } \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/Serialization/WidgetConverter.cs b/src/dymaptic.GeoBlazor.Core/Serialization/WidgetConverter.cs index 68b30d5d2..09225ad11 100644 --- a/src/dymaptic.GeoBlazor.Core/Serialization/WidgetConverter.cs +++ b/src/dymaptic.GeoBlazor.Core/Serialization/WidgetConverter.cs @@ -62,7 +62,7 @@ internal class WidgetConverter : JsonConverter return null; default: // look for the type in GeoBlazor Pro - string typeName = + string typeName = $"dymaptic.GeoBlazor.Pro.Components.Widgets.{typeValue.ToString()!.KebabToPascalCase()}Widget"; try @@ -88,6 +88,6 @@ internal class WidgetConverter : JsonConverter public override void Write(Utf8JsonWriter writer, Widget value, JsonSerializerOptions options) { - writer.WriteRawValue(JsonSerializer.Serialize(value, typeof(object), options)); + JsonSerializer.Serialize(writer, value, typeof(object), options); } -} +} \ No newline at end of file diff --git a/src/dymaptic.GeoBlazor.Core/badge_fullmethodcoverage.svg b/src/dymaptic.GeoBlazor.Core/badge_fullmethodcoverage.svg index e0166c286..a72bcd487 100644 --- a/src/dymaptic.GeoBlazor.Core/badge_fullmethodcoverage.svg +++ b/src/dymaptic.GeoBlazor.Core/badge_fullmethodcoverage.svg @@ -1,4 +1,4 @@ - +