এসকিউএল সার্ভার ২০১ in সালে স্থানিক ডেটার জন্য মেকভালিড () এর বিকল্প


13

আমার কাছে ভৌগলিক LINESTRINGডেটাগুলির একটি খুব বড় টেবিল রয়েছে যা আমি ওরাকল থেকে এসকিউএল সার্ভারে চলে যাচ্ছি। ওরাকল-এ এই ডেটার বিরুদ্ধে কার্যকর হওয়া অনেকগুলি মূল্যায়ন রয়েছে এবং এসকিউএল সার্ভারেও এই ডেটাগুলির বিরুদ্ধে তাদের কার্যকর করতে হবে।

সমস্যা: এসকিউএল সার্ভারের LINESTRINGওরাকেলের চেয়ে বৈধের জন্য আরও কঠোর প্রয়োজনীয়তা রয়েছে ; "লাইন স্ট্রিং উদাহরণটি দুই বা ততোধিক পয়েন্টের ব্যবধানে নিজেকে ওভারল্যাপ করতে পারে না"। এটি ঠিক তাই ঘটে যে আমাদের শতকরা এক শতাংশ LINESTRINGসেই মানদণ্ডটি পূরণ করে না, যার অর্থ আমরা ডেটা মূল্যায়নের জন্য প্রয়োজনীয় ফাংশনগুলি ব্যর্থ হয়। আমার ডেটা সামঞ্জস্য করতে হবে যাতে এটি এসকিউএল সার্ভারে সফলভাবে যাচাই করা যায়।

উদাহরণ স্বরূপ:

একটি খুব সাধারণ LINESTRINGযা নিজেই দ্বিগুণ হয় বৈধকরণ :

select geography::STGeomFromText(
    'LINESTRING (0 0 1, 0 1 2, 0 -1 3)',4326).IsValidDetailed()
24413: Not valid because of two overlapping edges in curve (1).

এর MakeValidবিপরীতে কার্য সম্পাদন করা হচ্ছে:

select geography::STGeomFromText(
    'LINESTRING (0 0 1, 0 1 2, 0 -1 3)',4326).MakeValid().STAsText()
LINESTRING (0 -0.999999999999867, 0 0, 0 0.999999999999867)

দুর্ভাগ্যক্রমে MakeValidফাংশনটি পয়েন্টগুলির ক্রম পরিবর্তন করে এবং তৃতীয় মাত্রা সরিয়ে দেয়, এটি আমাদের জন্য অযোগ্য করে তোলে। আমি অন্য একটি পদ্ধতির সন্ধান করছি যা তৃতীয় মাত্রা পুনরায় অর্ডারিং বা অপসারণ ছাড়াই এই সমস্যার সমাধান করে।

কোন ধারনা?

আমার আসল তথ্যটিতে কয়েকশ / পয়েন্ট রয়েছে।

উত্তর:


12

আমাকে প্রথমবারের মতো এসকিউএল সার্ভারে স্থানিক ডেটা নিয়ে খেলছি (তাই আপনি সম্ভবত এটি প্রথম অংশটি জানেন) তবে এসকিউএল সার্ভার (xyz) স্থানাঙ্ককে সত্য হিসাবে চিকিত্সা করছে না তা বুঝতে আমাকে বেশ সময় লাগল 3 ডি মান, এটি তাদের (অক্ষাংশ দ্রাঘিমাংশ) হিসাবে একটি alচ্ছিক "উচ্চতা" মান, জেড হিসাবে বিবেচনা করে যা বৈধতা এবং অন্যান্য ফাংশন দ্বারা উপেক্ষা করা হয়।

প্রমান:

select geography::STGeomFromText('LINESTRING (0 0 1, 0 1 2, 0 -1 3)', 4326)
    .IsValidDetailed()

24413: Not valid because of two overlapping edges in curve (1).

আপনার প্রথম উদাহরণটি আমার কাছে অদ্ভুত বলে মনে হয়েছিল কারণ (0 0 1), (0 1 2), এবং (0 -1 3) 3 ডি স্পেসে কোলাইনারি নয় (আমি একজন গণিতবিদ, তাই আমি এই পদগুলিতে ভাবছিলাম)। IsValidDetailed(এবং MakeValid) এগুলি (0 0), (0 1) এবং (0, -1) হিসাবে আচরণ করছে, যা একটি ওভারল্যাপিং লাইন তৈরি করে।

এটি প্রমাণ করতে, কেবল এক্স এবং জেড অদলবদল করুন এবং এটি বৈধতা দেয়:

select geography::STGeomFromText('LINESTRING (1 0 0, 2 1 0, 3 -1 0)', 4326)
    .IsValidDetailed()

24400: Valid

এটি গাণিতিক 3 ডি স্পেসের পয়েন্টের পরিবর্তে আমাদের পৃথিবীর পৃষ্ঠে চিহ্নিত অঞ্চল বা পথ হিসাবে যদি মনে হয় তবে এটি প্রকৃত অর্থে আসে।


আপনার সমস্যার দ্বিতীয় অংশটি হ'ল জেড (এবং এম) পয়েন্টের মানগুলি এসকিউএল দ্বারা ফাংশনগুলির মাধ্যমে সংরক্ষণ করা হয় না :

জেড-স্থানাঙ্কগুলি লাইব্রেরি দ্বারা তৈরি কোনও গণনায় ব্যবহৃত হয় না এবং কোনও লাইব্রেরি গণনার মধ্য দিয়ে বহন করা হয় না।

এটি দুর্ভাগ্যক্রমে নকশা দ্বারা। এটি মাইক্রোসফ্টকে 2010 সালে জানানো হয়েছিল, অনুরোধটি "উইল্ট ফিক্স" হিসাবে বন্ধ করা হয়েছিল। আপনি সেই আলোচনাটিকে প্রাসঙ্গিক মনে করতে পারেন, তাদের যুক্তিটি হ'ল:

জেড এবং এম নির্ধারণ করা অস্পষ্ট, কারণ মেকভালিড স্থানিক উপাদানগুলিকে বিভক্ত করে এবং মার্জ করে। পয়েন্টগুলি প্রায়শই এই প্রক্রিয়া চলাকালীন তৈরি, সরানো বা সরানো হয়। অতএব মেকভালিড (এবং অন্যান্য নির্মাণগুলি) জেড এবং এম এর মানকে হ্রাস করে।

উদাহরণ স্বরূপ:

DECLARE @a geometry = geometry::Parse('POINT(0 0 2 2)');
DECLARE @b geometry = geometry::Parse('POINT(0 0 1 1)');
SELECT @a.STUnion(@b).AsTextZM()

মান Z এবং এম বিন্দু (0 0) এর জন্য অস্পষ্ট। আমরা অর্ধ-সঠিক ফলাফলটি না ফেরার পরিবর্তে জেড এবং এমকে পুরোপুরি বাদ দেওয়ার সিদ্ধান্ত নিয়েছি।

আপনি ঠিক কীভাবে জানেন তা যদি আপনি পরে এগুলিকে নিয়োগ করতে পারেন। বিকল্পভাবে আপনি ইনপুটটিতে বৈধ হওয়ার জন্য আপনার জিনিসগুলি যেভাবে উত্পন্ন করেন তা পরিবর্তন করতে পারেন, বা আপনার অবজেক্টের দুটি সংস্করণ রাখতে পারেন, এটি বৈধ এবং অন্যটি যা আপনার সমস্ত বৈশিষ্ট্য সংরক্ষণ করে। যদি আপনি আপনার দৃশ্যের আরও ভালভাবে ব্যাখ্যা করেন এবং আপনি কী কী জিনিসগুলি নিয়ে করেন সম্ভবত আমরা আপনাকে অতিরিক্ত কাজের পরিমাণ দিতে সক্ষম হতে পারি।

তদতিরিক্ত, আপনি ইতিমধ্যে দেখেছেন, MakeValidঅন্যান্য অপ্রত্যাশিত কাজগুলিও করতে পারে , যেমন পয়েন্টের ক্রম পরিবর্তন করা, একটি মাল্টলাইনস্ট্রিং ফিরিয়ে দিতে, বা এমনকি কোনও পয়েন্ট অবজেক্ট ফেরত দেওয়া।


আমি যে ধারণাটি পেয়েছি সেগুলি হ'ল এগুলি পরিবর্তে একটি মাল্টিপোয়েন্ট অবজেক্ট হিসাবে সংরক্ষণ করা :

সমস্যাটি যখন তখন আপনার লাইনস্ট্রিংটি দুটি পয়েন্টের মধ্যে লাইনের একটি ধারাবাহিক বিভাগকে পূর্বে রেখার দ্বারা চিহ্নিত করে ret সংজ্ঞা অনুসারে, আপনি যদি বিদ্যমান পয়েন্টগুলি প্রত্যাহার করে থাকেন তবে লাইনস্ট্রিং আর সহজ জ্যামিতি নয় যা এই পয়েন্টসটি উপস্থাপন করতে পারে এবং মেকাভালিড () আপনাকে পরিবর্তে একটি মাল্টিলিনেস্ট্রিং দেবে (এবং আপনার জেড / এম মান হারাবে)।

দুর্ভাগ্যক্রমে, আপনি যদি জিপিএস ডেটা বা এর সাথে কাজ করে থাকেন তবে সম্ভবত এই পথটির কোনও সময়ে আপনি আপনার পথটি পিছনে ফেলেছেন সম্ভবত, তাই লাইনস্ট্রিংগুলি সবসময় এই পরিস্থিতিতে কার্যকর হয় না :( অবশ্যই, এই জাতীয় ডেটা হিসাবে সংরক্ষণ করা উচিত যেহেতু আপনার ডেটা সময়ে নিয়মিত পয়েন্টগুলিতে নমুনাযুক্ত কোনও অবজেক্টের বিচ্ছিন্ন অবস্থানের প্রতিনিধিত্ব করে a

আপনার ক্ষেত্রে এটি ঠিক জরিমানা করেছে:

select geometry::STGeomFromText('MULTIPOINT (0 0 1, 0 1 2, 0 -1 3)',4326)
    .IsValidDetailed()

24400: Valid

আপনার যদি একে একে লাইনস্ট্রিং হিসাবে বজায় রাখতে হয় তবে জেড সংরক্ষণ করার সময় আপনার নিজের সংস্করণটি MakeValidকিছুটা X বা Y পয়েন্টকে সামান্য সামঞ্জস্য করে লিখতে হবে (এবং অন্যান্য পাগল জিনিসগুলি যেমন করেন না এটিকে অন্য বস্তুর ধরণে রূপান্তর করুন)।

আমি এখনও কিছু কোড নিয়ে কাজ করছি, তবে এখানে কিছু প্রাথমিক ধারণাটি দেখুন:


সম্পাদনা করুন ঠিক আছে, পরীক্ষার সময় আমি কয়েকটি জিনিস পেয়েছি:

  • যদি জ্যামিতি অবজেক্টটি অবৈধ হয় তবে আপনি এটির সাথে খুব বেশি কিছু করতে পারবেন না। আপনি এটি পড়তে পারবেন না STGeometryType, আপনি সেগুলি মাধ্যমে পুনরাবৃত্তি করতে পারবেন না STNumPointsবা ব্যবহার করতে পারবেন না STPointN। যদি আপনি ব্যবহার করতে না পারেন তবে MakeValidআপনি মূলত ভৌগলিক অবজেক্টের পাঠ্য উপস্থাপনের জন্য অপারেটিং নিয়ে আটকে আছেন।
  • ব্যবহারের STAsText()ফলে এমনকি কোনও অবৈধ অবজেক্টের পাঠ্য উপস্থাপনাও ফিরে আসবে, তবে জেড বা এম মানগুলি দেয় না। পরিবর্তে, আমরা চাই AsTextZM()বা ToString()
  • আপনি কল করতে পারেন এমন ফাংশন তৈরি করতে পারবেন না RAND()(ফাংশনগুলিকে ডিটারমিনিস্টিক হওয়া দরকার), সুতরাং আমি কেবল এটিকে ক্রমাগত আরও বৃহত্তর এবং বৃহত্তর মানগুলিতে ঠেলে দিয়েছি। আপনার ডেটাটির যথার্থতা কী, বা ছোট পরিবর্তনগুলি কতটা সহনশীল তা আমার আসলেই ধারণা নেই, সুতরাং আপনার নিজের বিবেচনায় এই ফাংশনটি ব্যবহার বা সংশোধন করুন।

আমার কোনও ধারণা নেই যদি সেখানে কোনও সম্ভাব্য ইনপুট থাকে যা এই লুপটি চিরতরে চলে যেতে পারে। তোমাকে সতর্ক করা হইছে.

CREATE FUNCTION dbo.FixBadLineString (@input geography) RETURNS geography
AS BEGIN
DECLARE @output geography

IF @input.STIsValid() = 1   --send valid objects back as-is
  SET @output = @input;
ELSE IF LEFT(@input.IsValidDetailed(),6) = '24413:'
--"Not valid because of two overlapping edges in curve"
BEGIN
  --make a new MultiPoint object from the LineString text
  DECLARE @mp geography = geography::STGeomFromText(
      REPLACE(@input.AsTextZM(), 'LINESTRING', 'MULTIPOINT'), 4326);
  DECLARE @newText nvarchar(max); --to build output
  DECLARE @point int 
  DECLARE @tinynum float = 0;

  SET @output = @input;
  --keep going until it validates
  WHILE @output.STIsValid() = 0
  BEGIN
    SET @newText = 'LINESTRING (';
    SET @point = 1
    SET @tinynum = @tinynum + 0.00000001

    --Loop through the points, add a bit and append to the new string
    WHILE @point <= @mp.STNumPoints()
    BEGIN
      SET @newText = @newText + convert(varchar(50),
               @mp.STPointN(@point).Long + @tinynum) + ' ';
      SET @newText = @newText + convert(varchar(50),
               @mp.STPointN(@point).Lat - @tinynum) + ' ';
      SET @newText = @newText + convert(varchar(50), 
               @mp.STPointN(@point).Z) + ', ';
      SET @tinynum = @tinynum * -2
      SET @point = @point + 1
    END

    --close the parens and make the new LineString object
    SET @newText = LEFT(@newText, LEN(@newText) - 1) + ')'
    SET @output = geography::STGeomFromText(@newText, 4326);
  END; --this will loop if it is still invalid
  RETURN @output;
END;
--Any other unhandled error, just send back NULL
ELSE SET @output = NULL;

RETURN @output;
END

স্ট্রিংটিকে বিশ্লেষণের পরিবর্তে, MultiPointএকই পয়েন্টগুলির সেটটি ব্যবহার করে আমি একটি নতুন অবজেক্ট তৈরি করতে বেছে নিয়েছি , তাই আমি তাদের মাধ্যমে পুনরাবৃত্তি করতে এবং তাদেরকে ন্যাজ করতে পারি, তারপরে একটি নতুন লাইনস্ট্রিংকে পুনরায় সংযুক্ত করতে পারি। এটি পরীক্ষা করার জন্য এখানে কিছু কোড রয়েছে, এর মধ্যে 3 টি মান (আপনার নমুনা সহ) অবৈধ শুরু করে তবে তা স্থির করা হয়েছে:

declare @geostuff table (baddata geography)

INSERT INTO @geostuff (baddata)
          SELECT geography::STGeomFromText('LINESTRING (0 0 1, 0 1 2, 0 -1 3)',4326)
UNION ALL SELECT geography::STGeomFromText('LINESTRING (0 2 0, 0 1 0.5, 0 -1 -14)',4326)
UNION ALL SELECT geography::STGeomFromText('LINESTRING (0 0 4, 1 1 40, -1 -1 23)',4326)
UNION ALL SELECT geography::STGeomFromText('LINESTRING (1 1 9, 0 1 -.5, 0 -1 3)',4326)
UNION ALL SELECT geography::STGeomFromText('LINESTRING (6 6 26.5, 4 4 42, 12 12 86)',4326)
UNION ALL SELECT geography::STGeomFromText('LINESTRING (0 0 2, -4 4 -2, 4 -4 0)',4326)

SELECT baddata.AsTextZM() as before, baddata.IsValidDetailed() as pretest,
 dbo.FixBadLineString(baddata).AsTextZM() as after,
 dbo.FixBadLineString(baddata).IsValidDetailed() as posttest 
FROM @geostuff

দুর্দান্ত উত্তর, ধন্যবাদ ব্র্যাডিসি। আমি এটিকে আমার প্রশ্নের মধ্যে অন্তর্ভুক্ত করি নি, তবে আমার আসল তথ্যটিতে কয়েকশ / হাজার হাজার পয়েন্ট রয়েছে, সুতরাং "@ টিনিনাম * 2" টেকসই ছিল না। পরিবর্তে আমি "@ টিনিনাম" পুরোপুরি বাদ দিয়েছি এবং 0 এবং 0.000000003 এর মধ্যে একটি এলোমেলো সংখ্যা ব্যবহার করেছি। আমি ডেটাটির বিপরীতে এটি চালিয়ে যাচ্ছি এবং এখনও অবধি ২২ কে শেষ হয়েছে, সমস্ত লাইনরেটিং হিসাবে বৈধ হয়ে গেছে।
ক্যাপ্টেনলক

3

এই BradC এর FixBadLineStringফাংশন 0 এবং 0,000000003 মধ্যে একটি র্যান্ডম সংখ্যা ব্যবহার করতে tweaked ফলে এটির জন্য স্কেল করা হয়, যার ফলে LINESTRINGsপয়েন্ট সংখ্যক সঙ্গে, এবং এছাড়াও স্থানাঙ্ক পরিবর্তন কমানোর:

CREATE FUNCTION dbo.FixBadLineString (@input geography) RETURNS geography
AS BEGIN
DECLARE @output geography

IF @input.STIsValid() = 1   --send valid objects back as-is
  SET @output = @input;
ELSE IF LEFT(@input.IsValidDetailed(),6) = '24413:'
--"Not valid because of two overlapping edges in curve"
BEGIN
  --make a new MultiPoint object from the LineString text
  DECLARE @mp geography = geography::STGeomFromText(
      REPLACE(@input.AsTextZM(), 'LINESTRING', 'MULTIPOINT'), 4326);
  DECLARE @newText nvarchar(max); --to build output
  DECLARE @point int 

  SET @output = @input;
  --keep going until it validates
  WHILE @output.STIsValid() = 0
  BEGIN
    SET @newText = 'LINESTRING (';
    SET @point = 1

    --Loop through the points, add/subtract a random value between 0 and 3E-9 and append to the new string
    WHILE @point <= @mp.STNumPoints()
    BEGIN
      SET @newText = @newText + convert(varchar(50),
        CAST(@mp.STPointN(@point).Long AS NUMERIC(18,9)) + 
          CAST(ABS(CHECKSUM(PWDENCRYPT(N''))) / 644245094100000000 AS NUMERIC(18,9))) + ' ';
      SET @newText = @newText + convert(varchar(50),
        CAST(@mp.STPointN(@point).Lat AS NUMERIC(18,9)) - 
          CAST(ABS(CHECKSUM(PWDENCRYPT(N''))) / 644245094100000000 AS NUMERIC(18,9))) + ' ';
      SET @newText = @newText + convert(varchar(50), 
               @mp.STPointN(@point).Z) + ', ';
      SET @point = @point + 1
    END

    --close the parens and make the new LineString object
    SET @newText = LEFT(@newText, LEN(@newText) - 1) + ')'
    SET @output = geography::STGeomFromText(@newText, 4326);
  END; --this will loop if it is still invalid
  RETURN @output;
END;
--Any other unhandled error, just send back NULL
ELSE SET @output = NULL;

RETURN @output;
END

1
দেখতে অনেক ভাল লাগছে, আমি PWDENCRYPTফাংশন সম্পর্কে জানতাম না । আপনি বাদ দিতে পারতেন ABSএবং এটি ইতিবাচক বা নেতিবাচক সংখ্যাটি ফিরে আসতে পারত, তাই আমরা সবসময় এক্স-এ যোগ করি না এবং ওয়াই থেকে বিয়োগ করি না
ব্র্যাডিসি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.